merge: branch 'systemd' into main

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2115
This commit is contained in:
Beniamino Galvani
2025-02-04 15:25:34 +01:00
125 changed files with 4311 additions and 2112 deletions

View File

@@ -73,7 +73,7 @@ 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_;
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(

View File

@@ -84,9 +84,8 @@ struct sd_dhcp6_client {
bool send_release;
};
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, const struct in6_addr *address, const void *packet, size_t len);
int dhcp6_client_send_message(sd_dhcp6_client *client);
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);

View File

@@ -8,6 +8,7 @@
#include <inttypes.h>
#include "sd-dhcp6-lease.h"
#include "dns-resolver-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
@@ -38,6 +39,8 @@ struct sd_dhcp6_lease {
struct in6_addr *dns;
size_t dns_count;
sd_dns_resolver *dnr;
size_t n_dnr;
char **domains;
struct in6_addr *ntp;
size_t ntp_count;

View File

@@ -19,9 +19,10 @@
#include "fd-util.h"
#include "socket-util.h"
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *local_address) {
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
.in6.sin6_addr = *ASSERT_PTR(local_address),
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
.in6.sin6_scope_id = ifindex,
};
@@ -29,9 +30,6 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
int r;
assert(ifindex > 0);
assert(local_address);
src.in6.sin6_addr = *local_address;
s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0)
@@ -60,20 +58,14 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
return TAKE_FD(s);
}
int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
const void *packet, size_t len) {
int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
union sockaddr_union dest = {
.in6.sin6_family = AF_INET6,
.in6.sin6_addr = *ASSERT_PTR(server_address),
.in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
};
int r;
assert(server_address);
memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
if (r < 0)
if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)) < 0)
return -errno;
return 0;

View File

@@ -208,6 +208,7 @@ bool dhcp6_option_can_request(uint16_t option) {
case SD_DHCP6_OPTION_V6_DOTS_RI:
case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
case SD_DHCP6_OPTION_V6_DNR:
return true;
default:
return false;
@@ -822,74 +823,6 @@ int dhcp6_option_parse_addresses(
return 0;
}
static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
_cleanup_free_ char *domain = NULL;
const uint8_t *optval;
size_t optlen, n = 0;
int r;
assert(data);
assert(len);
assert(*data || *len == 0);
assert(ret);
optval = *data;
optlen = *len;
if (optlen <= 1)
return -ENODATA;
for (;;) {
const char *label;
uint8_t c;
if (optlen == 0)
break;
c = *optval;
optval++;
optlen--;
if (c == 0)
/* End label */
break;
if (c > 63)
return -EBADMSG;
if (c > optlen)
return -EMSGSIZE;
/* Literal label */
label = (const char*) optval;
optval += c;
optlen -= c;
if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM;
if (n != 0)
domain[n++] = '.';
r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
return r;
n += r;
}
if (n > 0) {
if (!GREEDY_REALLOC(domain, n + 1))
return -ENOMEM;
domain[n] = '\0';
}
*ret = TAKE_PTR(domain);
*data = optval;
*len = optlen;
return n;
}
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
_cleanup_free_ char *domain = NULL;
int r;
@@ -897,7 +830,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **r
assert(optval || optlen == 0);
assert(ret);
r = parse_domain(&optval, &optlen, &domain);
r = dns_name_from_wire_format(&optval, &optlen, &domain);
if (r < 0)
return r;
if (r == 0)
@@ -924,11 +857,11 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha
while (optlen > 0) {
_cleanup_free_ char *name = NULL;
r = parse_domain(&optval, &optlen, &name);
r = dns_name_from_wire_format(&optval, &optlen, &name);
if (r < 0)
return r;
if (r == 0)
continue;
if (dns_name_is_root(name)) /* root domain */
return -EBADMSG;
r = strv_consume(&names, TAKE_PTR(name));
if (r < 0)

View File

@@ -28,9 +28,11 @@ typedef struct DHCP6Message DHCP6Message;
#define DHCP6_MIN_OPTIONS_SIZE \
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
#define IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS \
((const struct in6_addr) { { { \
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, \
} } } )
enum {
DHCP6_PORT_SERVER = 547,
@@ -150,9 +152,9 @@ typedef enum DHCP6FQDNFlag {
DHCP6_FQDN_FLAG_N = 1 << 2,
} DHCP6FQDNFlag;
const char *dhcp6_state_to_string(DHCP6State s) _const_;
const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
const char* dhcp6_state_to_string(DHCP6State s) _const_;
const char* dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_;
const char *dhcp6_message_status_to_string(DHCP6Status s) _const_;
const char* dhcp6_message_status_to_string(DHCP6Status s) _const_;
DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_;
int dhcp6_message_status_to_errno(DHCP6Status s);

View File

@@ -3,7 +3,7 @@
#include "nm-sd-adapt-core.h"
#include "env-util.h"
#include "format-util.h"
#include "format-ifname.h"
#include "network-common.h"
#include "socket-util.h"
#include "unaligned.h"

View File

@@ -749,8 +749,6 @@ static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *
int dhcp6_client_send_message(sd_dhcp6_client *client) {
_cleanup_free_ uint8_t *buf = NULL;
struct in6_addr all_servers =
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
struct sd_dhcp6_option *j;
usec_t elapsed_usec, time_now;
be16_t elapsed_time;
@@ -845,7 +843,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
r = dhcp6_network_send_udp_socket(client->fd, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS, buf, offset);
if (r < 0)
return r;
@@ -1336,7 +1334,7 @@ static int client_receive_message(
return 0;
}
if ((size_t) len < sizeof(DHCP6Message)) {
log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
log_dhcp6_client(client, "Too small to be DHCPv6 message: ignoring");
return 0;
}
@@ -1412,7 +1410,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
r = client_send_release(client);
if (r < 0)
log_dhcp6_client_errno(client, r,
"Failed to send DHCP6 release message, ignoring: %m");
"Failed to send DHCPv6 release message, ignoring: %m");
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
@@ -1423,7 +1421,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
}
int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
if (!client)
return false;
return client->state != DHCP6_STATE_STOPPED;
}

View File

@@ -10,7 +10,9 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "network-common.h"
#include "sort-util.h"
#include "strv.h"
#include "unaligned.h"
@@ -426,7 +428,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
if (r < 0)
return r;
return strv_extend_strv(&lease->domains, domains, true);
return strv_extend_strv_consume(&lease->domains, TAKE_PTR(domains), /* filter_duplicates = */ true);
}
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
@@ -440,6 +442,105 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
return strv_length(lease->domains);
}
#if 0 /* NM_IGNORED */
static int dhcp6_lease_add_dnr(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
assert(lease);
_cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
size_t offset = 0;
/* priority */
if (optlen - offset < sizeof(uint16_t))
return -EBADMSG;
res.priority = unaligned_read_be16(optval + offset);
offset += sizeof(uint16_t);
/* adn */
if (optlen - offset < sizeof(uint16_t))
return -EBADMSG;
size_t ilen = unaligned_read_be16(optval + offset);
offset += sizeof(uint16_t);
if (offset + ilen > optlen)
return -EBADMSG;
r = dhcp6_option_parse_domainname(optval + offset, ilen, &res.auth_name);
if (r < 0)
return r;
r = dns_name_is_valid_ldh(res.auth_name);
if (r < 0)
return r;
if (!r)
return -EBADMSG;
offset += ilen;
/* RFC9463 § 3.1.6: adn only mode */
if (offset == optlen)
return 0;
/* addrs */
if (optlen - offset < sizeof(uint16_t))
return -EBADMSG;
ilen = unaligned_read_be16(optval + offset);
offset += sizeof(uint16_t);
if (offset + ilen > optlen)
return -EBADMSG;
_cleanup_free_ struct in6_addr *addrs = NULL;
size_t n_addrs = 0;
r = dhcp6_option_parse_addresses(optval + offset, ilen, &addrs, &n_addrs);
if (r < 0)
return r;
if (n_addrs == 0)
return -EBADMSG;
offset += ilen;
res.addrs = new(union in_addr_union, n_addrs);
if (!res.addrs)
return -ENOMEM;
for (size_t i = 0; i < n_addrs; i++) {
union in_addr_union addr = {.in6 = addrs[i]};
/* RFC9463 § 6.2 client MUST discard multicast and host loopback addresses */
if (in_addr_is_multicast(AF_INET6, &addr) ||
in_addr_is_localhost(AF_INET6, &addr))
return -EBADMSG;
res.addrs[i] = addr;
}
res.n_addrs = n_addrs;
res.family = AF_INET6;
/* svc params */
r = dnr_parse_svc_params(optval + offset, optlen-offset, &res);
if (r < 0)
return r;
/* Append this resolver */
if (!GREEDY_REALLOC(lease->dnr, lease->n_dnr+1))
return -ENOMEM;
lease->dnr[lease->n_dnr++] = TAKE_STRUCT(res);
typesafe_qsort(lease->dnr, lease->n_dnr, dns_resolver_prio_compare);
return 1;
}
int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->dnr)
return -ENODATA;
*ret = lease->dnr;
return lease->n_dnr;
}
#endif /* NM_IGNORED */
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
@@ -765,8 +866,7 @@ static int dhcp6_lease_parse_message(
continue;
}
dhcp6_ia_free(lease->ia_na);
lease->ia_na = TAKE_PTR(ia);
free_and_replace_full(lease->ia_na, ia, dhcp6_ia_free);
break;
}
case SD_DHCP6_OPTION_IA_PD: {
@@ -790,8 +890,7 @@ static int dhcp6_lease_parse_message(
continue;
}
dhcp6_ia_free(lease->ia_pd);
lease->ia_pd = TAKE_PTR(ia);
free_and_replace_full(lease->ia_pd, ia, dhcp6_ia_free);
break;
}
case SD_DHCP6_OPTION_RAPID_COMMIT:
@@ -852,7 +951,16 @@ static int dhcp6_lease_parse_message(
irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
break;
#if 0 /* NM_IGNORED */
case SD_DHCP6_OPTION_V6_DNR:
r = dhcp6_lease_add_dnr(lease, optval, optlen);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to parse DNR option, ignoring: %m");
if (r == 0)
log_dhcp6_client(client, "Received ADN-only DNRv6 option, ignoring.");
break;
#endif /* NM_IGNORED */
case SD_DHCP6_OPTION_VENDOR_OPTS:
r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
if (r < 0)
@@ -906,6 +1014,9 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
dhcp6_ia_free(lease->ia_na);
dhcp6_ia_free(lease->ia_pd);
free(lease->dns);
#if 0 /* NM_IGNORED */
dns_resolver_done_many(lease->dnr, lease->n_dnr);
#endif /* NM_IGNORED */
free(lease->fqdn);
free(lease->captive_portal);
strv_free(lease->domains);

View File

@@ -714,7 +714,7 @@ static int device_tag(sd_device *device, const char *tag, bool add) {
assert(device);
assert(tag);
r = device_get_device_id(device, &id);
r = sd_device_get_device_id(device, &id);
if (r < 0)
return r;
@@ -801,7 +801,7 @@ static int device_get_db_path(sd_device *device, char **ret) {
assert(device);
assert(ret);
r = device_get_device_id(device, &id);
r = sd_device_get_device_id(device, &id);
if (r < 0)
return r;

View File

@@ -20,10 +20,12 @@ int device_opendir(sd_device *device, const char *subdir, DIR **ret);
int device_get_property_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
return device_get_sysattr_unsigned_full(device, sysattr, 0, ret_value);
}
int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
int device_get_devnode_mode(sd_device *device, mode_t *ret);
int device_get_devnode_uid(sd_device *device, uid_t *ret);
@@ -73,5 +75,5 @@ int device_read_uevent_file(sd_device *device);
int device_set_action(sd_device *device, sd_device_action_t a);
sd_device_action_t device_action_from_string(const char *s) _pure_;
const char *device_action_to_string(sd_device_action_t a) _const_;
const char* device_action_to_string(sd_device_action_t a) _const_;
void dump_device_action_table(void);

View File

@@ -100,7 +100,7 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
assert(st);
return devname_from_devnum(st->st_mode, st->st_rdev, ret);
}
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname);
char** device_make_log_fields(sd_device *device);

View File

@@ -243,7 +243,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
r = path_simplify_alloc(_syspath, &syspath);
if (r < 0)
return r;
return log_oom_debug();
}
assert_se(devpath = startswith(syspath, "/sys"));
@@ -354,9 +354,11 @@ _public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) {
assert_return(ret, -EINVAL);
assert_return(ifname, -EINVAL);
if (ifname_valid(ifname)) {
r = device_new_from_main_ifname(ret, ifname);
if (r >= 0)
return r;
}
r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL);
if (r < 0)
@@ -393,25 +395,79 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
return 0;
}
static int device_strjoin_new(
static int device_new_from_path_join(
sd_device **device,
const char *subsystem,
const char *driver_subsystem,
const char *sysname,
const char *a,
const char *b,
const char *c,
const char *d,
sd_device **ret) {
const char *d) {
const char *p;
_cleanup_(sd_device_unrefp) sd_device *new_device = NULL;
_cleanup_free_ char *p = NULL;
int r;
p = strjoina(a, b, c, d);
if (access(p, F_OK) < 0)
return IN_SET(errno, ENOENT, ENAMETOOLONG) ? 0 : -errno; /* If this sysfs is too long then it doesn't exist either */
assert(device);
assert(sysname);
r = sd_device_new_from_syspath(ret, p);
p = path_join(a, b, c, d);
if (!p)
return -ENOMEM;
r = sd_device_new_from_syspath(&new_device, p);
if (r == -ENODEV)
return 0;
if (r < 0)
return r;
/* Check if the found device really has the expected subsystem and sysname, for safety. */
if (!device_in_subsystem(new_device, subsystem))
return 0;
const char *new_driver_subsystem = NULL;
(void) sd_device_get_driver_subsystem(new_device, &new_driver_subsystem);
if (!streq_ptr(driver_subsystem, new_driver_subsystem))
return 0;
const char *new_sysname;
r = sd_device_get_sysname(new_device, &new_sysname);
if (r < 0)
return r;
if (!streq(sysname, new_sysname))
return 0;
/* If this is the first device we found, then take it. */
if (!*device) {
*device = TAKE_PTR(new_device);
return 1;
}
/* Unfortunately, (subsystem, sysname) pair is not unique. For examples,
* - /sys/bus/gpio and /sys/class/gpio, both have gpiochip%N. However, these point to different devpaths.
* - /sys/bus/mdio_bus and /sys/class/mdio_bus,
* - /sys/bus/mei and /sys/class/mei,
* - /sys/bus/typec and /sys/class/typec, and so on.
* Hence, if we already know a device, then we need to check if it is equivalent to the newly found one. */
const char *devpath, *new_devpath;
r = sd_device_get_devpath(*device, &devpath);
if (r < 0)
return r;
r = sd_device_get_devpath(new_device, &new_devpath);
if (r < 0)
return r;
if (!streq(devpath, new_devpath))
return log_debug_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
"sd-device: found multiple devices for subsystem=%s and sysname=%s, refusing: %s, %s",
subsystem, sysname, devpath, new_devpath);
return 1; /* Fortunately, they are consistent. */
}
_public_ int sd_device_new_from_subsystem_sysname(
@@ -419,6 +475,7 @@ _public_ int sd_device_new_from_subsystem_sysname(
const char *subsystem,
const char *sysname) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
char *name;
int r;
@@ -437,19 +494,15 @@ _public_ int sd_device_new_from_subsystem_sysname(
if (streq(subsystem, "subsystem")) {
FOREACH_STRING(s, "/sys/bus/", "/sys/class/") {
r = device_strjoin_new(s, name, NULL, NULL, ret);
r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, s, name, NULL, NULL);
if (r < 0)
return r;
if (r > 0)
return 0;
}
} else if (streq(subsystem, "module")) {
r = device_strjoin_new("/sys/module/", name, NULL, NULL, ret);
r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, "/sys/module/", name, NULL, NULL);
if (r < 0)
return r;
if (r > 0)
return 0;
} else if (streq(subsystem, "drivers")) {
const char *sep;
@@ -461,35 +514,33 @@ _public_ int sd_device_new_from_subsystem_sysname(
sep++;
if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret);
r = device_new_from_path_join(&device, subsystem, subsys, "drivers", "/sys/bus/", subsys, "/drivers", NULL);
else
r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
r = device_new_from_path_join(&device, subsystem, subsys, sep, "/sys/bus/", subsys, "/drivers/", sep);
if (r < 0)
return r;
if (r > 0)
return 0;
}
}
r = device_strjoin_new("/sys/bus/", subsystem, "/devices/", name, ret);
r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, "/sys/bus/", subsystem, "/devices/", name);
if (r < 0)
return r;
if (r > 0)
return 0;
r = device_strjoin_new("/sys/class/", subsystem, "/", name, ret);
r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, "/sys/class/", subsystem, name, NULL);
if (r < 0)
return r;
if (r > 0)
return 0;
r = device_strjoin_new("/sys/firmware/", subsystem, "/", name, ret);
/* Note that devices under /sys/firmware/ (e.g. /sys/firmware/devicetree/base/) do not have
* subsystem. Hence, pass NULL for subsystem. See issue #35861. */
r = device_new_from_path_join(&device, /* subsystem = */ NULL, /* driver_subsystem = */ NULL, sysname, "/sys/firmware/", subsystem, name, NULL);
if (r < 0)
return r;
if (r > 0)
return 0;
if (!device)
return -ENODEV;
*ret = TAKE_PTR(device);
return 0;
}
_public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
@@ -499,10 +550,8 @@ _public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st
return device_new_from_mode_and_devnum(ret, st->st_mode, st->st_rdev);
}
_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
struct stat st;
dev_t devnum;
mode_t mode;
static int device_new_from_devname(sd_device **ret, const char *devname, bool strict) {
int r;
assert_return(ret, -EINVAL);
assert_return(devname, -EINVAL);
@@ -510,28 +559,41 @@ _public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
/* This function actually accepts both devlinks and devnames, i.e. both symlinks and device
* nodes below /dev/. */
/* Also ignore when the specified path is "/dev". */
if (isempty(path_startswith(devname, "/dev")))
if (strict && isempty(path_startswith(devname, "/dev/")))
return -EINVAL;
dev_t devnum;
mode_t mode;
if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0)
/* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified.
* In that case, we can directly convert the path to syspath, hence it is not necessary
* that the specified path exists. So, this works fine without udevd being running. */
return device_new_from_mode_and_devnum(ret, mode, devnum);
if (stat(devname, &st) < 0)
return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno;
_cleanup_free_ char *resolved = NULL;
struct stat st;
r = chase_and_stat(devname, /* root = */ NULL, /* flags = */ 0, &resolved, &st);
if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
return -ENODEV;
if (r < 0)
return r;
if (isempty(path_startswith(resolved, "/dev/")))
return -EINVAL;
return sd_device_new_from_stat_rdev(ret, &st);
}
_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
return device_new_from_devname(ret, devname, /* strict = */ true);
}
_public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
assert_return(ret, -EINVAL);
assert_return(path, -EINVAL);
if (path_startswith(path, "/dev"))
return sd_device_new_from_devname(ret, path);
if (device_new_from_devname(ret, path, /* strict = */ false) >= 0)
return 0;
return device_new_from_syspath(ret, path, /* strict = */ false);
}
@@ -1211,6 +1273,20 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
}
#if 0 /* NM_IGNORED */
_public_ int sd_device_get_driver_subsystem(sd_device *device, const char **ret) {
assert_return(device, -EINVAL);
if (!device_in_subsystem(device, "drivers"))
return -ENOENT;
assert(device->driver_subsystem);
if (ret)
*ret = device->driver_subsystem;
return 0;
}
_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
int r;
@@ -1226,7 +1302,7 @@ _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
if (devtype)
*devtype = device->devtype;
return !!device->devtype;
return 0;
}
_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *device, const char *subsystem, const char *devtype, sd_device **ret) {
@@ -1645,9 +1721,8 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
}
}
int device_get_device_id(sd_device *device, const char **ret) {
assert(device);
assert(ret);
_public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
assert_return(device, -EINVAL);
if (!device->device_id) {
_cleanup_free_ char *id = NULL;
@@ -1697,6 +1772,7 @@ int device_get_device_id(sd_device *device, const char **ret) {
device->device_id = TAKE_PTR(id);
}
if (ret)
*ret = device->device_id;
return 0;
}
@@ -2428,7 +2504,7 @@ int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_valu
return v > 0;
}
int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value) {
const char *value;
int r;
@@ -2437,7 +2513,7 @@ int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned
return r;
unsigned v;
r = safe_atou(value, &v);
r = safe_atou_full(value, base, &v);
if (r < 0)
return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);

View File

@@ -189,6 +189,9 @@ struct inode_data {
* iteration. */
int fd;
/* The path that the fd points to. The field is optional. */
char *path;
/* The inotify "watch descriptor" */
int wd;

View File

@@ -171,4 +171,13 @@ int event_add_child_pidref(
return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
}
dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
assert(e);
assert(ts);
assert_se(sd_event_now(e, CLOCK_REALTIME, &ts->realtime) >= 0);
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
return ts;
}
#endif /* NM_IGNORED */

View File

@@ -37,4 +37,6 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
#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);
dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts);
#endif /* NM_IGNORED */

View File

@@ -27,9 +27,11 @@
#include "missing_magic.h"
#include "missing_syscall.h"
#include "missing_threads.h"
#include "missing_wait.h"
#include "origin-id.h"
#include "path-util.h"
#include "prioq.h"
#include "pidfd-util.h"
#include "process-util.h"
#include "psi-util.h"
#include "set.h"
@@ -184,7 +186,7 @@ static thread_local sd_event *default_event = NULL;
static void source_disconnect(sd_event_source *s);
static void event_gc_inode_data(sd_event *e, struct inode_data *d);
static sd_event *event_resolve(sd_event *e) {
static sd_event* event_resolve(sd_event *e) {
return e == SD_EVENT_DEFAULT ? default_event : e;
}
@@ -340,7 +342,7 @@ static void free_clock_data(struct clock_data *d) {
prioq_free(d->latest);
}
static sd_event *event_free(sd_event *e) {
static sd_event* event_free(sd_event *e) {
sd_event_source *s;
assert(e);
@@ -445,7 +447,7 @@ fail:
}
/* Define manually so we can add the origin check */
_public_ sd_event *sd_event_ref(sd_event *e) {
_public_ sd_event* sd_event_ref(sd_event *e) {
if (!e)
return NULL;
if (event_origin_changed(e))
@@ -473,8 +475,13 @@ _public_ sd_event* sd_event_unref(sd_event *e) {
_unused_ _cleanup_(sd_event_unrefp) sd_event *_ref = sd_event_ref(e);
_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
if (s)
(void) sd_event_source_set_enabled(s, SD_EVENT_OFF);
int r;
r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
if (r < 0)
log_debug_errno(r, "Failed to disable event source %p (%s): %m",
s, strna(s->description));
return sd_event_source_unref(s);
}
@@ -1072,6 +1079,8 @@ static void source_disconnect(sd_event_source *s) {
}
static sd_event_source* source_free(sd_event_source *s) {
int r;
assert(s);
source_disconnect(s);
@@ -1085,23 +1094,12 @@ static sd_event_source* source_free(sd_event_source *s) {
if (s->child.process_owned) {
if (!s->child.exited) {
bool sent = false;
if (s->child.pidfd >= 0) {
if (pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0) < 0) {
if (errno == ESRCH) /* Already dead */
sent = true;
else if (!ERRNO_IS_NOT_SUPPORTED(errno))
log_debug_errno(errno, "Failed to kill process " PID_FMT " via pidfd_send_signal(), re-trying via kill(): %m",
s->child.pid);
} else
sent = true;
}
if (!sent)
if (kill(s->child.pid, SIGKILL) < 0)
if (errno != ESRCH) /* Already dead */
log_debug_errno(errno, "Failed to kill process " PID_FMT " via kill(), ignoring: %m",
if (s->child.pidfd >= 0)
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
else
r = RET_NERRNO(kill(s->child.pid, SIGKILL));
if (r < 0 && r != -ESRCH)
log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
s->child.pid);
}
@@ -1109,6 +1107,9 @@ static sd_event_source* source_free(sd_event_source *s) {
siginfo_t si = {};
/* Reap the child if we can */
if (s->child.pidfd >= 0)
(void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
else
(void) waitid(P_PID, s->child.pid, &si, WEXITED);
}
}
@@ -1179,7 +1180,7 @@ static int source_set_pending(sd_event_source *s, bool b) {
return 1;
}
static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) {
static sd_event_source* source_new(sd_event *e, bool floating, EventSourceType type) {
/* Let's allocate exactly what we need. Note that the difference of the smallest event source
* structure to the largest is 144 bytes on x86-64 at the time of writing, i.e. more than two cache
@@ -1577,11 +1578,6 @@ static int child_exit_callback(sd_event_source *s, const siginfo_t *si, void *us
return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
}
static bool shall_use_pidfd(void) {
/* Mostly relevant for debugging, i.e. this is used in test-event.c to test the event loop once with and once without pidfd */
return secure_getenv_bool("SYSTEMD_PIDFD") != 0;
}
_public_ int sd_event_add_child(
sd_event *e,
sd_event_source **ret,
@@ -1629,34 +1625,29 @@ _public_ int sd_event_add_child(
if (!s)
return -ENOMEM;
/* We always take a pidfd here if we can, even if we wait for anything else than WEXITED, so that we
* pin the PID, and make regular waitid() handling race-free. */
s->child.pidfd = pidfd_open(pid, 0);
if (s->child.pidfd < 0)
return -errno;
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
s->wakeup = WAKEUP_EVENT_SOURCE;
s->child.options = options;
s->child.callback = callback;
s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT;
/* We always take a pidfd here if we can, even if we wait for anything else than WEXITED, so that we
* pin the PID, and make regular waitid() handling race-free. */
if (shall_use_pidfd()) {
s->child.pidfd = pidfd_open(pid, 0);
if (s->child.pidfd < 0) {
/* Propagate errors unless the syscall is not supported or blocked */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
} else
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
} else
s->child.pidfd = -EBADF;
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We have a pidfd and we only want to watch for exit */
/* We only want to watch for exit */
r = source_child_pidfd_register(s, s->enabled);
if (r < 0)
return r;
} else {
/* We have no pidfd or we shall wait for some other event than WEXITED */
/* We shall wait for some other event than WEXITED */
r = event_make_signal_data(e, SIGCHLD, NULL);
if (r < 0)
return r;
@@ -1686,7 +1677,6 @@ _public_ int sd_event_add_child_pidfd(
sd_event_child_handler_t callback,
void *userdata) {
_cleanup_(source_freep) sd_event_source *s = NULL;
pid_t pid;
int r;
@@ -1727,17 +1717,12 @@ _public_ int sd_event_add_child_pidfd(
s->wakeup = WAKEUP_EVENT_SOURCE;
s->child.pidfd = pidfd;
s->child.pid = pid;
s->child.options = options;
s->child.callback = callback;
s->child.pidfd_owned = false; /* If we got the pidfd passed in we don't own it by default (similar to the IO fd case) */
s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT;
r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
if (r < 0)
return r;
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We only want to watch for WEXITED */
r = source_child_pidfd_register(s, s->enabled);
@@ -1752,6 +1737,11 @@ _public_ int sd_event_add_child_pidfd(
e->need_process_child = true;
}
r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
if (r < 0)
return r;
s->child.pid = pid;
e->n_online_child_sources++;
if (ret)
@@ -2280,6 +2270,7 @@ static void event_free_inode_data(
assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
}
free(d->path);
free(d);
}
@@ -2423,7 +2414,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
if (wd < 0)
return -errno;
return wd;
if (d->wd < 0) {
r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
@@ -2445,6 +2436,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
return 1;
}
#if 0 /* NM_IGNORED */
static int inotify_exit_callback(sd_event_source *s, const struct inotify_event *event, void *userdata) {
assert(s);
@@ -2520,6 +2512,15 @@ static int event_add_inotify_fd_internal(
}
LIST_PREPEND(to_close, e->inode_data_to_close_list, inode_data);
_cleanup_free_ char *path = NULL;
r = fd_get_path(inode_data->fd, &path);
if (r < 0 && r != -ENOSYS) { /* The path is optional, hence ignore -ENOSYS. */
event_gc_inode_data(e, inode_data);
return r;
}
free_and_replace(inode_data->path, path);
}
/* Link our event source to the inode data object */
@@ -2579,6 +2580,7 @@ _public_ int sd_event_add_inotify(
return r;
}
#endif /* NM_IGNORED */
static sd_event_source* event_source_free(sd_event_source *s) {
if (!s)
@@ -2609,18 +2611,18 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des
return free_and_strdup(&s->description, description);
}
_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
_public_ int sd_event_source_get_description(sd_event_source *s, const char **ret) {
assert_return(s, -EINVAL);
assert_return(description, -EINVAL);
assert_return(ret, -EINVAL);
if (!s->description)
return -ENXIO;
*description = s->description;
*ret = s->description;
return 0;
}
_public_ sd_event *sd_event_source_get_event(sd_event_source *s) {
_public_ sd_event* sd_event_source_get_event(sd_event_source *s) {
assert_return(s, NULL);
assert_return(!event_origin_changed(s->event), NULL);
@@ -2645,7 +2647,7 @@ _public_ int sd_event_source_get_io_fd(sd_event_source *s) {
}
_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
int r;
int saved_fd, r;
assert_return(s, -EINVAL);
assert_return(fd >= 0, -EBADF);
@@ -2655,16 +2657,12 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
if (s->io.fd == fd)
return 0;
if (event_source_is_offline(s)) {
s->io.fd = fd;
s->io.registered = false;
} else {
int saved_fd;
saved_fd = s->io.fd;
assert(s->io.registered);
s->io.fd = fd;
assert(event_source_is_offline(s) == !s->io.registered);
if (s->io.registered) {
s->io.registered = false;
r = source_io_register(s, s->enabled, s->io.events);
@@ -2677,6 +2675,9 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
(void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
}
if (s->io.owned)
safe_close(saved_fd);
return 0;
}
@@ -2697,13 +2698,13 @@ _public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
return 0;
}
_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t *ret) {
assert_return(s, -EINVAL);
assert_return(events, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
*events = s->io.events;
*ret = s->io.events;
return 0;
}
@@ -2735,14 +2736,14 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events)
return 0;
}
_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) {
_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret) {
assert_return(s, -EINVAL);
assert_return(revents, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM);
assert_return(s->pending, -ENODATA);
assert_return(!event_origin_changed(s->event), -ECHILD);
*revents = s->io.revents;
*ret = s->io.revents;
return 0;
}
@@ -2754,11 +2755,12 @@ _public_ int sd_event_source_get_signal(sd_event_source *s) {
return s->signal.sig;
}
_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) {
_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *ret) {
assert_return(s, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(!event_origin_changed(s->event), -ECHILD);
*priority = s->priority;
*ret = s->priority;
return 0;
}
@@ -2806,6 +2808,13 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
}
LIST_PREPEND(to_close, s->event->inode_data_to_close_list, new_inode_data);
_cleanup_free_ char *path = NULL;
r = fd_get_path(new_inode_data->fd, &path);
if (r < 0 && r != -ENOSYS)
goto fail;
free_and_replace(new_inode_data->path, path);
}
/* Move the event source to the new inode data structure */
@@ -3092,13 +3101,13 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
return 0;
}
_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) {
_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *ret) {
assert_return(s, -EINVAL);
assert_return(usec, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
*usec = s->time.next;
*ret = s->time.next;
return 0;
}
@@ -3142,13 +3151,13 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec
return sd_event_source_set_time(s, usec);
}
_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *ret) {
assert_return(s, -EINVAL);
assert_return(usec, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
*usec = s->time.accuracy;
*ret = s->time.accuracy;
return 0;
}
@@ -3174,23 +3183,23 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec
return 0;
}
_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) {
_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *ret) {
assert_return(s, -EINVAL);
assert_return(clock, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
*clock = event_source_type_to_clock(s->type);
*ret = event_source_type_to_clock(s->type);
return 0;
}
_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *ret) {
assert_return(s, -EINVAL);
assert_return(pid, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
*pid = s->child.pid;
*ret = s->child.pid;
return 0;
}
@@ -3225,11 +3234,9 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
if (si)
copy = *si;
if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0) {
/* Let's propagate the error only if the system call is not implemented or prohibited */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0)
return -errno;
} else
return 0;
}
@@ -3290,13 +3297,29 @@ _public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own)
return 0;
}
_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret) {
assert_return(s, -EINVAL);
assert_return(mask, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_INOTIFY, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
*mask = s->inotify.mask;
*ret = s->inotify.mask;
return 0;
}
_public_ int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret) {
assert_return(s, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_INOTIFY, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
if (!s->inotify.inode_data)
return -ESTALE; /* already disconnected. */
if (!s->inotify.inode_data->path)
return -ENOSYS; /* /proc was not mounted? */
*ret = s->inotify.inode_data->path;
return 0;
}
@@ -3839,7 +3862,8 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i
if (_unlikely_(n != sizeof(si)))
return -EIO;
assert(SIGNAL_VALID(si.ssi_signo));
if (_unlikely_(!SIGNAL_VALID(si.ssi_signo)))
return -EIO;
if (e->signal_sources)
s = e->signal_sources[si.ssi_signo];
@@ -4540,7 +4564,7 @@ static int epoll_wait_usec(
/* epoll_pwait2() was added to Linux 5.11 (2021-02-14) and to glibc in 2.35 (2022-02-03). In contrast
* to other syscalls we don't bother with our own fallback syscall wrappers on old libcs, since this
* is not that obvious to implement given the libc and kernel definitions differ in the last
* argument. Moreover, the only reason to use it is the more accurate time-outs (which is not a
* argument. Moreover, the only reason to use it is the more accurate timeouts (which is not a
* biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's
* missing. */
@@ -4825,13 +4849,13 @@ _public_ int sd_event_dispatch(sd_event *e) {
static void event_log_delays(sd_event *e) {
char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p;
size_t l, i;
size_t l;
p = b;
l = sizeof(b);
for (i = 0; i < ELEMENTSOF(e->delays); i++) {
l = strpcpyf(&p, l, "%u ", e->delays[i]);
e->delays[i] = 0;
FOREACH_ELEMENT(delay, e->delays) {
l = strpcpyf(&p, l, "%u ", *delay);
*delay = 0;
}
log_debug("Event loop iterations: %s", b);
}
@@ -4892,7 +4916,6 @@ _public_ int sd_event_loop(sd_event *e) {
assert_return(!event_origin_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
PROTECT_EVENT(e);
while (e->state != SD_EVENT_FINISHED) {
@@ -4920,7 +4943,7 @@ _public_ int sd_event_get_state(sd_event *e) {
return e->state;
}
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
_public_ int sd_event_get_exit_code(sd_event *e, int *ret) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_origin_changed(e), -ECHILD);
@@ -4928,8 +4951,8 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
if (!e->exit_requested)
return -ENODATA;
if (code)
*code = e->exit_code;
if (ret)
*ret = e->exit_code;
return 0;
}
@@ -4945,10 +4968,10 @@ _public_ int sd_event_exit(sd_event *e, int code) {
return 0;
}
_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *ret) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(usec, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD);
if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock))
@@ -4956,11 +4979,11 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
if (!triple_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran before and thus have no cached time. */
*usec = now(clock);
*ret = now(clock);
return 1;
}
*usec = triple_timestamp_by_clock(&e->timestamp, clock);
*ret = triple_timestamp_by_clock(&e->timestamp, clock);
return 0;
}
@@ -4989,18 +5012,17 @@ _public_ int sd_event_default(sd_event **ret) {
}
#if 0 /* NM_IGNORED */
_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
_public_ int sd_event_get_tid(sd_event *e, pid_t *ret) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(tid, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD);
if (e->tid != 0) {
*tid = e->tid;
return 0;
}
if (e->tid == 0)
return -ENXIO;
*ret = e->tid;
return 0;
}
_public_ int sd_event_set_watchdog(sd_event *e, int b) {
@@ -5230,6 +5252,9 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
int r;
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_origin_changed(e), -ECHILD);
if (b) {
/* We want to maintain pointers to these event sources, so that we can destroy them when told
@@ -5241,7 +5266,7 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
if (r < 0)
return r;
assert(sd_event_source_set_floating(e->sigint_event_source, true) >= 0);
assert_se(sd_event_source_set_floating(e->sigint_event_source, true) >= 0);
change = true;
}
@@ -5249,26 +5274,26 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
r = sd_event_add_signal(e, &e->sigterm_event_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0) {
if (change) {
assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
assert_se(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
}
return r;
}
assert(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0);
assert_se(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0);
change = true;
}
} else {
if (e->sigint_event_source) {
assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
assert_se(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
change = true;
}
if (e->sigterm_event_source) {
assert(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0);
assert_se(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0);
e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
change = true;
}

View File

@@ -11,6 +11,8 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
#include "namespace-util.h"
#include "process-util.h"
#include "sha256.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -273,4 +275,65 @@ sd_id128_t id128_digest(const void *data, size_t size) {
return id128_make_v4_uuid(id);
}
int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret) {
_cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
pid_t pid, child;
sd_id128_t id;
ssize_t k;
int r;
assert(ret);
if (isempty(machine))
return sd_id128_get_boot(ret);
r = container_get_leader(machine, &pid);
if (r < 0)
return r;
r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
if (r < 0)
return r;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
pidnsfd, mntnsfd, -1, -1, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
pair[0] = safe_close(pair[0]);
r = id128_get_boot(&id);
if (r < 0)
_exit(EXIT_FAILURE);
k = send(pair[1], &id, sizeof(id), MSG_NOSIGNAL);
if (k != sizeof(id))
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
pair[1] = safe_close(pair[1]);
r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
return -EIO;
k = recv(pair[0], &id, sizeof(id), 0);
if (k != sizeof(id))
return -EIO;
if (sd_id128_is_null(id))
return -EIO;
*ret = id;
return 0;
}
#endif /* NM_IGNORED */

View File

@@ -49,6 +49,9 @@ int id128_get_product(sd_id128_t *ret);
sd_id128_t id128_digest(const void *data, size_t size);
int id128_get_boot(sd_id128_t *ret);
int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret);
/* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
IN_SET(r, \

View File

@@ -15,6 +15,7 @@
#include "hmac.h"
#include "id128-util.h"
#include "io-util.h"
#include "keyring-util.h"
#include "macro.h"
#include "missing_syscall.h"
#include "missing_threads.h"
@@ -176,14 +177,24 @@ int id128_get_machine(const char *root, sd_id128_t *ret) {
}
#endif /* NM_IGNORED */
int id128_get_boot(sd_id128_t *ret) {
int r;
assert(ret);
r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, ret);
if (r == -ENOENT && proc_mounted() == 0)
return -ENOSYS;
return r;
}
_public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id = {};
int r;
if (sd_id128_is_null(saved_boot_id)) {
r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id);
if (r == -ENOENT && proc_mounted() == 0)
return -ENOSYS;
r = id128_get_boot(&saved_boot_id);
if (r < 0)
return r;
}
@@ -199,7 +210,6 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
char *d, *p, *g, *u, *e;
unsigned long perms;
key_serial_t key;
size_t sz = 256;
uid_t uid;
gid_t gid;
int r, c;
@@ -218,24 +228,9 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
return -errno;
}
for (;;) {
description = new(char, sz);
if (!description)
return -ENOMEM;
c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
if (c < 0)
return -errno;
if ((size_t) c <= sz)
break;
sz = c;
free(description);
}
/* The kernel returns a final NUL in the string, verify that. */
assert(description[c-1] == 0);
r = keyring_describe(key, &description);
if (r < 0)
return r;
/* Chop off the final description string */
d = strrchr(description, ';');
@@ -387,4 +382,17 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
return sd_id128_get_app_specific(id, app_id, ret);
}
_public_ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
sd_id128_t id;
int r;
assert_return(ret, -EINVAL);
r = sd_id128_get_invocation(&id);
if (r < 0)
return r;
return sd_id128_get_app_specific(id, app_id, ret);
}
#endif /* NM_IGNORED */

View File

@@ -45,6 +45,10 @@ typedef void (*_sd_destroy_t)(void *userdata);
# define _sd_pure_ __attribute__((__pure__))
#endif
#ifndef _sd_const_
# define _sd_const_ __attribute__((__const__))
#endif
/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
* it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
* (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
@@ -105,4 +109,11 @@ typedef void (*_sd_destroy_t)(void *userdata);
_SD_##id##_INT64_MIN = INT64_MIN, \
_SD_##id##_INT64_MAX = INT64_MAX
/* In GCC 14 (C23) we can force enums to have the right types, and not solely rely on language extensions anymore */
#if ((__GNUC__ >= 14) || (__STDC_VERSION__ >= 202311L)) && !defined(__cplusplus)
# define _SD_ENUM_TYPE_S64(id) id : int64_t
#else
# define _SD_ENUM_TYPE_S64(id) id
#endif
#endif

View File

@@ -34,7 +34,7 @@ typedef struct sd_device sd_device;
typedef struct sd_device_enumerator sd_device_enumerator;
typedef struct sd_device_monitor sd_device_monitor;
__extension__ typedef enum sd_device_action_t {
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_device_action_t) {
SD_DEVICE_ADD,
SD_DEVICE_REMOVE,
SD_DEVICE_CHANGE,
@@ -74,6 +74,7 @@ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *su
int sd_device_get_syspath(sd_device *device, const char **ret);
int sd_device_get_subsystem(sd_device *device, const char **ret);
int sd_device_get_driver_subsystem(sd_device *device, const char **ret);
int sd_device_get_devtype(sd_device *device, const char **ret);
int sd_device_get_devnum(sd_device *device, dev_t *devnum);
int sd_device_get_ifindex(sd_device *device, int *ifindex);
@@ -85,21 +86,22 @@ int sd_device_get_sysnum(sd_device *device, const char **ret);
int sd_device_get_action(sd_device *device, sd_device_action_t *ret);
int sd_device_get_seqnum(sd_device *device, uint64_t *ret);
int sd_device_get_diskseq(sd_device *device, uint64_t *ret);
int sd_device_get_device_id(sd_device *device, const char **ret);
int sd_device_get_is_initialized(sd_device *device);
int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret);
int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret);
const char *sd_device_get_tag_first(sd_device *device);
const char *sd_device_get_tag_next(sd_device *device);
const char *sd_device_get_current_tag_first(sd_device *device);
const char *sd_device_get_current_tag_next(sd_device *device);
const char *sd_device_get_devlink_first(sd_device *device);
const char *sd_device_get_devlink_next(sd_device *device);
const char *sd_device_get_property_first(sd_device *device, const char **value);
const char *sd_device_get_property_next(sd_device *device, const char **value);
const char *sd_device_get_sysattr_first(sd_device *device);
const char *sd_device_get_sysattr_next(sd_device *device);
const char* sd_device_get_tag_first(sd_device *device);
const char* sd_device_get_tag_next(sd_device *device);
const char* sd_device_get_current_tag_first(sd_device *device);
const char* sd_device_get_current_tag_next(sd_device *device);
const char* sd_device_get_devlink_first(sd_device *device);
const char* sd_device_get_devlink_next(sd_device *device);
const char* sd_device_get_property_first(sd_device *device, const char **value);
const char* sd_device_get_property_next(sd_device *device, const char **value);
const char* sd_device_get_sysattr_first(sd_device *device);
const char* sd_device_get_sysattr_next(sd_device *device);
sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix);
sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix);
@@ -135,6 +137,7 @@ int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, c
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator);
/* device monitor */
@@ -142,6 +145,9 @@ int sd_device_monitor_new(sd_device_monitor **ret);
sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m);
sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m);
int sd_device_monitor_get_fd(sd_device_monitor *m);
int sd_device_monitor_get_events(sd_device_monitor *m);
int sd_device_monitor_get_timeout(sd_device_monitor *m, uint64_t *ret);
int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size);
int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event);
int sd_device_monitor_detach_event(sd_device_monitor *m);
@@ -149,8 +155,10 @@ sd_event *sd_device_monitor_get_event(sd_device_monitor *m);
sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m);
int sd_device_monitor_set_description(sd_device_monitor *m, const char *description);
int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret);
int sd_device_monitor_is_running(sd_device_monitor *m);
int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata);
int sd_device_monitor_stop(sd_device_monitor *m);
int sd_device_monitor_receive(sd_device_monitor *m, sd_device **ret);
int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype);
int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag);

View File

@@ -30,6 +30,7 @@
_SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
typedef struct sd_dns_resolver sd_dns_resolver;
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);
@@ -74,6 +75,7 @@ int sd_dhcp6_lease_get_pd_lifetime_timestamp(
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_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **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);

View File

@@ -165,8 +165,9 @@ enum {
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_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143, /* RFC 6153 */
SD_DHCP6_OPTION_V6_DNR = 144 /* RFC 9463 */
/* option codes 145-65535 are unassigned */
};
_SD_END_DECLARATIONS;

View File

@@ -108,12 +108,12 @@ int sd_event_run(sd_event *e, uint64_t usec);
int sd_event_loop(sd_event *e);
int sd_event_exit(sd_event *e, int code);
int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec);
int sd_event_now(sd_event *e, clockid_t clock, uint64_t *ret);
int sd_event_get_fd(sd_event *e);
int sd_event_get_state(sd_event *e);
int sd_event_get_tid(sd_event *e, pid_t *tid);
int sd_event_get_exit_code(sd_event *e, int *code);
int sd_event_get_tid(sd_event *e, pid_t *ret);
int sd_event_get_exit_code(sd_event *e, int *ret);
int sd_event_set_watchdog(sd_event *e, int b);
int sd_event_get_watchdog(sd_event *e);
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
@@ -123,33 +123,33 @@ sd_event_source* sd_event_source_ref(sd_event_source *s);
sd_event_source* sd_event_source_unref(sd_event_source *s);
sd_event_source* sd_event_source_disable_unref(sd_event_source *s);
sd_event *sd_event_source_get_event(sd_event_source *s);
sd_event* sd_event_source_get_event(sd_event_source *s);
void* sd_event_source_get_userdata(sd_event_source *s);
void* sd_event_source_set_userdata(sd_event_source *s, void *userdata);
int sd_event_source_set_description(sd_event_source *s, const char *description);
int sd_event_source_get_description(sd_event_source *s, const char **description);
int sd_event_source_get_description(sd_event_source *s, const char **ret);
int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback);
int sd_event_source_get_pending(sd_event_source *s);
int sd_event_source_get_priority(sd_event_source *s, int64_t *priority);
int sd_event_source_get_priority(sd_event_source *s, int64_t *ret);
int sd_event_source_set_priority(sd_event_source *s, int64_t priority);
int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
int sd_event_source_get_enabled(sd_event_source *s, int *ret);
int sd_event_source_set_enabled(sd_event_source *s, int enabled);
int sd_event_source_get_io_fd(sd_event_source *s);
int sd_event_source_set_io_fd(sd_event_source *s, int fd);
int sd_event_source_get_io_fd_own(sd_event_source *s);
int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
int sd_event_source_get_io_events(sd_event_source *s, uint32_t *ret);
int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);
int sd_event_source_get_time(sd_event_source *s, uint64_t *usec);
int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret);
int sd_event_source_get_time(sd_event_source *s, uint64_t *ret);
int sd_event_source_set_time(sd_event_source *s, uint64_t usec);
int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec);
int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec);
int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *ret);
int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec);
int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *ret);
int sd_event_source_get_signal(sd_event_source *s);
int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
int sd_event_source_get_child_pid(sd_event_source *s, pid_t *ret);
int sd_event_source_get_child_pidfd(sd_event_source *s);
int sd_event_source_get_child_pidfd_own(sd_event_source *s);
int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own);
@@ -161,6 +161,7 @@ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo
int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
#endif
int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret);
int sd_event_source_set_memory_pressure_type(sd_event_source *e, const char *ty);
int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec);
int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);

View File

@@ -37,8 +37,8 @@ union sd_id128 {
#define SD_ID128_STRING_MAX 33U
#define SD_ID128_UUID_STRING_MAX 37U
char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]);
char* sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
char* sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]);
int sd_id128_from_string(const char *s, sd_id128_t *ret);
#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
@@ -53,6 +53,7 @@ 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);
int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
@@ -116,24 +117,24 @@ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_MAKE_UUID_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
#a #b #c #d "-" #e #f "-" #g #h "-" #i #j "-" #k #l #m #n #o #p
_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
_sd_const_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
return a.qwords[0] == b.qwords[0] && a.qwords[1] == b.qwords[1];
}
int sd_id128_string_equal(const char *s, sd_id128_t id);
_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
_sd_const_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
return a.qwords[0] == 0 && a.qwords[1] == 0;
}
_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
_sd_const_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
}
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
_sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
_sd_const_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
for (;;) {
sd_id128_t b = va_arg(ap, sd_id128_t);
@@ -145,7 +146,7 @@ _sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
}
}
_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
_sd_const_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
va_list ap;
int r;

View File

@@ -26,7 +26,9 @@
#include <sys/types.h>
#include "sd-event.h"
#include "sd-ndisc-neighbor.h"
#include "sd-ndisc-protocol.h"
#include "sd-ndisc-redirect.h"
#include "sd-ndisc-router.h"
#include "_sd-common.h"
@@ -35,9 +37,11 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_ndisc sd_ndisc;
__extension__ typedef enum sd_ndisc_event_t {
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_ndisc_event_t) {
SD_NDISC_EVENT_TIMEOUT,
SD_NDISC_EVENT_ROUTER,
SD_NDISC_EVENT_NEIGHBOR,
SD_NDISC_EVENT_REDIRECT,
_SD_NDISC_EVENT_MAX,
_SD_NDISC_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(NDISC_EVENT)

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/chattr-util.c',
'src/basic/btrfs.c',
'src/basic/devnum-util.c',
'src/basic/env-file.c',
@@ -14,6 +15,7 @@ libnm_systemd_shared = static_library(
'src/basic/extract-word.c',
'src/basic/fd-util.c',
'src/basic/fileio.c',
'src/basic/format-ifname.c',
'src/basic/format-util.c',
'src/basic/fs-util.c',
'src/basic/glyph-util.c',
@@ -31,10 +33,12 @@ libnm_systemd_shared = static_library(
'src/basic/ordered-set.c',
'src/basic/parse-util.c',
'src/basic/path-util.c',
'src/basic/pidfd-util.c',
'src/basic/prioq.c',
'src/basic/process-util.c',
'src/basic/random-util.c',
'src/basic/ratelimit.c',
'src/basic/sha256.c',
'src/basic/signal-util.c',
'src/basic/socket-util.c',
'src/basic/stat-util.c',
@@ -45,7 +49,7 @@ libnm_systemd_shared = static_library(
'src/basic/time-util.c',
'src/basic/tmpfile-util.c',
'src/basic/utf8.c',
'src/fundamental/sha256.c',
'src/fundamental/sha256-fundamental.c',
'src/fundamental/string-util-fundamental.c',
'src/shared/dns-domain.c',
'src/shared/web-util.c',

View File

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

View File

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

View File

@@ -116,6 +116,11 @@ raw_getpid(void)
#define ALTIFNAMSIZ 128
#endif
/* kernel cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
#ifndef PID_FS_MAGIC
#define PID_FS_MAGIC 0x50494446
#endif
#define HAVE_LINUX_TIME_TYPES_H 0
#ifndef __COMPAR_FN_T

View File

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

View File

@@ -45,7 +45,7 @@ void* greedy_realloc(
size_t need,
size_t size) {
size_t a, newalloc;
size_t newalloc;
void *q;
assert(p);
@@ -62,14 +62,13 @@ void* greedy_realloc(
return NULL;
newalloc = need * 2;
if (size_multiply_overflow(newalloc, size))
if (!MUL_ASSIGN_SAFE(&newalloc, size))
return NULL;
a = newalloc * size;
if (a < 64) /* Allocate at least 64 bytes */
a = 64;
if (newalloc < 64) /* Allocate at least 64 bytes */
newalloc = 64;
q = realloc(*p, a);
q = realloc(*p, newalloc);
if (!q)
return NULL;

View File

@@ -26,23 +26,23 @@ typedef void* (*mfree_func_t)(void *p);
#define alloca_safe(n) \
({ \
size_t _nn_ = n; \
size_t _nn_ = (n); \
assert(_nn_ <= ALLOCA_MAX); \
alloca(_nn_ == 0 ? 1 : _nn_); \
}) \
#define newa(t, n) \
({ \
size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \
(t*) alloca_safe(sizeof(t)*_n_); \
size_t _n_ = (n); \
assert_se(MUL_ASSIGN_SAFE(&_n_, sizeof(t))); \
(t*) alloca_safe(_n_); \
})
#define newa0(t, n) \
({ \
size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \
(t*) alloca0((sizeof(t)*_n_)); \
size_t _n_ = (n); \
assert_se(MUL_ASSIGN_SAFE(&_n_, sizeof(t))); \
(t*) alloca0(_n_); \
})
#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
@@ -155,7 +155,10 @@ void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_fr
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]))
({ \
const typeof(*(array)) *_from_ = (from); \
greedy_realloc_append((void**) &(array), &(n_array), _from_, (n_from), sizeof((array)[0])); \
})
#define alloca0(n) \
({ \

View File

@@ -4,7 +4,7 @@
#include <inttypes.h>
#include <stddef.h>
const char *arphrd_to_name(int id);
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

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "macro.h"
/* Bit index (0-based) to mask of specified type. Assertion failure if index is out of range. */
#define _INDEX_TO_MASK(type, i, uniq) \
({ \
int UNIQ_T(_i, uniq) = (i); \
assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \
((type)1) << UNIQ_T(_i, uniq); \
})
#define INDEX_TO_MASK(type, i) \
({ \
assert_cc(sizeof(type) <= sizeof(unsigned long long)); \
assert_cc(__builtin_choose_expr(__builtin_constant_p(i), i, 0) < (int)(sizeof(type) * 8)); \
__builtin_choose_expr(__builtin_constant_p(i), \
((type)1) << (i), \
_INDEX_TO_MASK(type, i, UNIQ)); \
})
/* Builds a mask of specified type with multiple bits set. Note the result will not be constant, even if all
* indexes are constant. */
#define INDEXES_TO_MASK(type, ...) \
UNIQ_INDEXES_TO_MASK(type, UNIQ, ##__VA_ARGS__)
#define UNIQ_INDEXES_TO_MASK(type, uniq, ...) \
({ \
typeof(type) UNIQ_T(_mask, uniq) = (type)0; \
int UNIQ_T(_i, uniq); \
FOREACH_ARGUMENT(UNIQ_T(_i, uniq), ##__VA_ARGS__) \
UNIQ_T(_mask, uniq) |= INDEX_TO_MASK(type, UNIQ_T(_i, uniq)); \
UNIQ_T(_mask, uniq); \
})
/* Same as the FLAG macros, but accept a 0-based bit index instead of a mask. Results in assertion failure if
* index is out of range for the type. */
#define SET_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), true)
#define CLEAR_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), false)
#define BIT_SET(bits, i) FLAGS_SET(bits, INDEX_TO_MASK(typeof(bits), i))
/* As above, but accepts multiple indexes. Note the result will not be constant, even if all indexes are
* constant. */
#define SET_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), true)
#define CLEAR_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), false)
#define BITS_SET(bits, ...) FLAGS_SET(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__))
/* Iterate through each set bit. Index is 0-based and type int. */
#define BIT_FOREACH(index, bits) _BIT_FOREACH(index, bits, UNIQ)
#define _BIT_FOREACH(index, bits, uniq) \
for (int UNIQ_T(_last, uniq) = -1, index; \
(index = BIT_NEXT_SET(bits, UNIQ_T(_last, uniq))) >= 0; \
UNIQ_T(_last, uniq) = index)
/* Find the next set bit after 0-based index 'prev'. Result is 0-based index of next set bit, or -1 if no
* more bits are set. */
#define BIT_FIRST_SET(bits) BIT_NEXT_SET(bits, -1)
#define BIT_NEXT_SET(bits, prev) \
UNIQ_BIT_NEXT_SET(bits, prev, UNIQ)
#define UNIQ_BIT_NEXT_SET(bits, prev, uniq) \
({ \
typeof(bits) UNIQ_T(_bits, uniq) = (bits); \
int UNIQ_T(_prev, uniq) = (prev); \
int UNIQ_T(_next, uniq); \
_BIT_NEXT_SET(UNIQ_T(_bits, uniq), \
UNIQ_T(_prev, uniq), \
UNIQ_T(_next, uniq)); \
})
#define _BIT_NEXT_SET(bits, prev, next) \
((int)(prev + 1) == (int)sizeof(bits) * 8 \
? -1 /* Prev index was msb. */ \
: ((next = __builtin_ffsll(((unsigned long long)(bits)) >> (prev + 1))) == 0 \
? -1 /* No more bits set. */ \
: prev + next))

View File

@@ -180,20 +180,33 @@ typedef enum CGroupUnified {
* generate paths with multiple adjacent / removed.
*/
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_path_open(const char *controller, const char *path);
int cg_cgroupid_open(int fsfd, uint64_t id);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
int cg_read_subgroup(DIR *d, char **ret);
int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret);
int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret);
static inline int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
return cg_get_cgroupid_at(AT_FDCWD, path, ret);
}
static inline int cg_fd_get_cgroupid(int fd, uint64_t *ret) {
return cg_get_cgroupid_at(fd, NULL, ret);
}
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0,
CGROUP_IGNORE_SELF = 1 << 1,
CGROUP_REMOVE = 1 << 2,
CGROUP_DONT_SKIP_UNMAPPED = 1 << 2,
CGROUP_NO_PIDFD = 1 << 3,
} CGroupFlags;
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags);
int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags);
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 **ret);
int cg_read_subgroup(DIR *d, char **ret);
typedef int (*cg_kill_log_func_t)(const PidRef *pid, int sig, void *userdata);
int cg_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
@@ -209,8 +222,6 @@ int cg_get_path_and_check(const char *controller, const char *path, const char *
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 *path);
int cg_is_delegated(const char *path);
@@ -258,15 +269,11 @@ int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
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);
int cg_is_empty(const char *controller, const char *path);
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 **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);
@@ -280,7 +287,9 @@ int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_
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 **ret_session);
int cg_pidref_get_session(const PidRef *pidref, char **ret);
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret);
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);
@@ -292,14 +301,12 @@ 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);
char *cg_unescape(const char *p) _pure_;
char* cg_unescape(const char *p) _pure_;
bool cg_controller_is_valid(const char *p);
int cg_slice_to_path(const char *unit, char **ret);
typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);
int cg_mask_supported(CGroupMask *ret);
int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
@@ -352,5 +359,10 @@ typedef union {
uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
} cg_file_handle;
#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
#define CG_FILE_HANDLE_INIT \
(cg_file_handle) { \
.file_handle.handle_bytes = sizeof(uint64_t), \
.file_handle.handle_type = FILEID_KERNFS, \
}
#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)

View File

@@ -27,12 +27,10 @@ typedef enum ChaseFlags {
* also points to the result path even if this flag is set.
* When this specified, chase() will succeed with 1 even if the
* file points to the last path component does not exist. */
CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. This
* needs to be set with CHASE_NONEXISTENT and/or CHASE_PARENT.
* Note, chase_and_open() or friends always add CHASE_PARENT flag
* when internally call chase(), hence CHASE_MKDIR_0755 can be
* safely set without CHASE_NONEXISTENT and CHASE_PARENT. */
CHASE_MKDIR_0755 = 1 << 11, /* Create any missing directories in the given path. */
CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */
CHASE_MUST_BE_DIRECTORY = 1 << 13, /* Fail if returned inode fd is not a dir */
CHASE_MUST_BE_REGULAR = 1 << 14, /* Fail if returned inode fd is not a regular file */
} ChaseFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);

View File

@@ -0,0 +1,176 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/fs.h>
#include "bitfield.h"
#include "chattr-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "string-util.h"
int chattr_full(
int dir_fd,
const char *path,
unsigned value,
unsigned mask,
unsigned *ret_previous,
unsigned *ret_final,
ChattrApplyFlags flags) {
_cleanup_close_ int fd = -EBADF;
unsigned old_attr, new_attr;
int set_flags_errno = 0;
struct stat st;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd < 0)
return fd;
if (fstat(fd, &st) < 0)
return -errno;
/* Explicitly check whether this is a regular file or directory. If it is anything else (such
* as a device node or fifo), then the ioctl will not hit the file systems but possibly
* drivers, where the ioctl might have different effects. Notably, DRM is using the same
* ioctl() number. */
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
return -ENOTTY;
if (mask == 0 && !ret_previous && !ret_final)
return 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
return -errno;
new_attr = (old_attr & ~mask) | (value & mask);
if (new_attr == old_attr) {
if (ret_previous)
*ret_previous = old_attr;
if (ret_final)
*ret_final = old_attr;
return 0;
}
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) >= 0) {
unsigned attr;
/* Some filesystems (BTRFS) silently fail when a flag cannot be set. Let's make sure our
* changes actually went through by querying the flags again and verifying they're equal to
* the flags we tried to configure. */
if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
return -errno;
if (new_attr == attr) {
if (ret_previous)
*ret_previous = old_attr;
if (ret_final)
*ret_final = new_attr;
return 1;
}
/* Trigger the fallback logic. */
errno = EINVAL;
}
if ((errno != EINVAL && !ERRNO_IS_NOT_SUPPORTED(errno)) ||
!FLAGS_SET(flags, CHATTR_FALLBACK_BITWISE))
return -errno;
/* When -EINVAL is returned, we assume that incompatible attributes are simultaneously
* specified. E.g., compress(c) and nocow(C) attributes cannot be set to files on btrfs.
* As a fallback, let's try to set attributes one by one.
*
* Also, when we get EOPNOTSUPP (or a similar error code) we assume a flag might just not be
* supported, and we can ignore it too */
unsigned current_attr = old_attr;
BIT_FOREACH(i, mask) {
unsigned new_one, mask_one = 1u << i;
new_one = UPDATE_FLAG(current_attr, mask_one, FLAGS_SET(value, mask_one));
if (new_one == current_attr)
continue;
if (ioctl(fd, FS_IOC_SETFLAGS, &new_one) < 0) {
if (!ERRNO_IS_IOCTL_NOT_SUPPORTED(errno))
return -errno;
log_full_errno(FLAGS_SET(flags, CHATTR_WARN_UNSUPPORTED_FLAGS) ? LOG_WARNING : LOG_DEBUG,
errno,
"Unable to set file attribute 0x%x on %s, ignoring: %m", mask_one, strna(path));
/* Ensures that we record whether only EOPNOTSUPP&friends are encountered, or if a more serious
* error (thus worth logging at a different level, etc) was seen too. */
if (set_flags_errno == 0 || !ERRNO_IS_NOT_SUPPORTED(errno))
set_flags_errno = -errno;
continue;
}
if (ioctl(fd, FS_IOC_GETFLAGS, &current_attr) < 0)
return -errno;
}
if (ret_previous)
*ret_previous = old_attr;
if (ret_final)
*ret_final = current_attr;
/* -ENOANO indicates that some attributes cannot be set. ERRNO_IS_NOT_SUPPORTED indicates that all
* encountered failures were due to flags not supported by the FS, so return a specific error in
* that case, so callers can handle it properly (e.g.: tmpfiles.d can use debug level logging). */
return current_attr == new_attr ? 1 : ERRNO_IS_NOT_SUPPORTED(set_flags_errno) ? set_flags_errno : -ENOANO;
}
int read_attr_fd(int fd, unsigned *ret) {
struct stat st;
assert(fd >= 0);
assert(ret);
if (fstat(fd, &st) < 0)
return -errno;
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
return -ENOTTY;
_cleanup_close_ int fd_close = -EBADF;
fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY, O_PATH, &fd_close); /* drop O_PATH if it is set */
if (fd < 0)
return fd;
return RET_NERRNO(ioctl(fd, FS_IOC_GETFLAGS, ret));
}
int read_attr_at(int dir_fd, const char *path, unsigned *ret) {
_cleanup_close_ int fd_close = -EBADF;
int fd;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret);
if (isempty(path) && dir_fd != AT_FDCWD)
fd = dir_fd;
else {
fd_close = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd_close < 0)
return fd_close;
fd = fd_close;
}
return read_attr_fd(fd, ret);
}

View File

@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <fcntl.h>
#include <linux/fs.h>
#include <stdbool.h>
#include <stddef.h>
#include "missing_fs.h"
/* The chattr() flags to apply when creating a new file *before* writing to it. In particular, flags such as
* FS_NOCOW_FL don't work if applied a-posteriori. All other flags are fine (or even necessary, think
* FS_IMMUTABLE_FL!) to apply after writing to the files. */
#define CHATTR_EARLY_FL \
(FS_NOATIME_FL | \
FS_COMPR_FL | \
FS_NOCOW_FL | \
FS_NOCOMP_FL | \
FS_PROJINHERIT_FL)
#define CHATTR_ALL_FL \
(FS_NOATIME_FL | \
FS_SYNC_FL | \
FS_DIRSYNC_FL | \
FS_APPEND_FL | \
FS_COMPR_FL | \
FS_NODUMP_FL | \
FS_EXTENT_FL | \
FS_IMMUTABLE_FL | \
FS_JOURNAL_DATA_FL | \
FS_SECRM_FL | \
FS_UNRM_FL | \
FS_NOTAIL_FL | \
FS_TOPDIR_FL | \
FS_NOCOW_FL | \
FS_PROJINHERIT_FL)
typedef enum ChattrApplyFlags {
CHATTR_FALLBACK_BITWISE = 1 << 0,
CHATTR_WARN_UNSUPPORTED_FLAGS = 1 << 1,
} ChattrApplyFlags;
int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) {
return chattr_full(dir_fd, path, value, mask, previous, NULL, 0);
}
static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
return chattr_full(fd, NULL, value, mask, previous, NULL, 0);
}
static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) {
return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0);
}
int read_attr_fd(int fd, unsigned *ret);
int read_attr_at(int dir_fd, const char *path, unsigned *ret);
/* Combination of chattr flags, that should be appropriate for secrets stored on disk: Secure Remove +
* Exclusion from Dumping + Synchronous Writing (i.e. not caching in memory) + In-Place Updating (i.e. not
* spurious copies). */
#define CHATTR_SECRET_FLAGS (FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL)
static inline int chattr_secret(int fd, ChattrApplyFlags flags) {
return chattr_full(fd, NULL, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE);
}

View File

@@ -42,9 +42,6 @@
#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC)
#define DEFAULT_START_LIMIT_BURST 5
/* Wait for 1.5 seconds at maximum for freeze operation */
#define FREEZE_TIMEOUT (1500 * USEC_PER_MSEC)
/* The default time after which exit-on-idle services exit. This
* should be kept lower than the watchdog timeout, because otherwise
* the watchdog pings will keep the loop busy. */
@@ -67,18 +64,12 @@
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0"
#define CONF_PATHS_USR(n) \
#define CONF_PATHS(n) \
"/etc/" n, \
"/run/" n, \
"/usr/local/lib/" n, \
"/usr/lib/" n
#define CONF_PATHS(n) \
CONF_PATHS_USR(n)
#define CONF_PATHS_USR_STRV(n) \
STRV_MAKE(CONF_PATHS_USR(n))
#define CONF_PATHS_STRV(n) \
STRV_MAKE(CONF_PATHS(n))
@@ -94,4 +85,5 @@
/* 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.systemd.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"
/* Recommended baseline - see README for details */
#define KERNEL_BASELINE_VERSION "5.7"

View File

@@ -60,21 +60,18 @@ int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
}
int device_path_make_inaccessible(mode_t mode, char **ret) {
char *s;
const char *s;
assert(ret);
if (S_ISCHR(mode))
s = strdup("/run/systemd/inaccessible/chr");
s = "/run/systemd/inaccessible/chr";
else if (S_ISBLK(mode))
s = strdup("/run/systemd/inaccessible/blk");
s = "/run/systemd/inaccessible/blk";
else
return -ENODEV;
if (!s)
return -ENOMEM;
*ret = s;
return 0;
return strdup_to(ret, s);
}
#if 0 /* NM_IGNORED */

View File

@@ -20,6 +20,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
#include "utf8.h"
#if 0 /* NM_IGNORED */
@@ -268,7 +269,7 @@ static bool env_entry_has_name(const char *entry, const char *name) {
return *t == '=';
}
char **strv_env_delete(char **x, size_t n_lists, ...) {
char** strv_env_delete(char **x, size_t n_lists, ...) {
size_t n, i = 0;
_cleanup_strv_free_ char **t = NULL;
va_list ap;
@@ -555,7 +556,7 @@ char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlag
}
#endif /* NM_IGNORED */
char *strv_env_pairs_get(char **l, const char *name) {
char* strv_env_pairs_get(char **l, const char *name) {
char *result = NULL;
assert(name);
@@ -568,7 +569,35 @@ char *strv_env_pairs_get(char **l, const char *name) {
}
#if 0 /* NM_IGNORED */
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
int strv_env_get_merged(char **l, char ***ret) {
_cleanup_strv_free_ char **v = NULL;
size_t n = 0;
int r;
assert(ret);
/* This converts a strv with pairs of environment variable name + value into a strv of name and
* value concatenated with a "=" separator. E.g.
* input : { "NAME", "value", "FOO", "var" }
* output : { "NAME=value", "FOO=var" } */
STRV_FOREACH_PAIR(key, value, l) {
char *s;
s = strjoin(*key, "=", *value);
if (!s)
return -ENOMEM;
r = strv_consume_with_size(&v, &n, s);
if (r < 0)
return r;
}
*ret = TAKE_PTR(v);
return 0;
}
char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
int k = 0;
STRV_FOREACH(p, e) {
@@ -800,10 +829,10 @@ int replace_env_full(
t = v;
}
r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
if (r < 0)
return r;
r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
if (r < 0)
return r;
@@ -935,21 +964,21 @@ int replace_env_argv(
return r;
n[++k] = NULL;
r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
r = strv_extend_strv_consume(&unset_variables, TAKE_PTR(u), /* filter_duplicates= */ true);
if (r < 0)
return r;
r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
r = strv_extend_strv_consume(&bad_variables, TAKE_PTR(b), /* filter_duplicates= */ true);
if (r < 0)
return r;
}
if (ret_unset_variables) {
strv_uniq(strv_sort(unset_variables));
strv_sort_uniq(unset_variables);
*ret_unset_variables = TAKE_PTR(unset_variables);
}
if (ret_bad_variables) {
strv_uniq(strv_sort(bad_variables));
strv_sort_uniq(bad_variables);
*ret_bad_variables = TAKE_PTR(bad_variables);
}
@@ -1146,14 +1175,17 @@ int setenvf(const char *name, bool overwrite, const char *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;
/* Try to suppress writes if the value is already set correctly (simply because memory management of
* environment variables sucks a bit. */
if (streq_ptr(getenv(name), value))
return 0;
return RET_NERRNO(setenv(name, value, overwrite));
}
#endif /* NM_IGNORED */

View File

@@ -34,14 +34,14 @@ int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_var
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);
char** strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
char** _strv_env_merge(char **first, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char** strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char** strv_env_unset(char **l, const char *p); /* In place ... */
char** strv_env_unset_many_internal(char **l, ...) _sentinel_;
@@ -59,7 +59,8 @@ 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_;
char* strv_env_pairs_get(char **l, const char *name) _pure_;
int strv_env_get_merged(char **l, char ***ret);
int getenv_bool(const char *p);
int secure_getenv_bool(const char *p);

View File

@@ -158,7 +158,7 @@ static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
}
_DEFINE_ABS_WRAPPER(RESOURCE);
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
/* Seven different errors for "operation/system call/socket feature not supported" */
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
return IN_SET(r,
-EOPNOTSUPP,
@@ -167,10 +167,17 @@ static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
-EAFNOSUPPORT,
-EPFNOSUPPORT,
-EPROTONOSUPPORT,
-ESOCKTNOSUPPORT);
-ESOCKTNOSUPPORT,
-ENOPROTOOPT);
}
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
/* ioctl() with unsupported command/arg might additionally return EINVAL */
static inline bool ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(intmax_t r) {
return ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EINVAL;
}
_DEFINE_ABS_WRAPPER(IOCTL_NOT_SUPPORTED);
/* Two different errors for access problems */
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
return IN_SET(r,

View File

@@ -368,6 +368,8 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
char *ans, *t, *prev, *prev2;
const char *f;
assert(s);
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
* reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
* unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
@@ -400,7 +402,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
if ((unsigned char) *f < ' ' ||
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
*f == '\\' || strchr(bad, *f)) {
*f == '\\' || (bad && strchr(bad, *f))) {
if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
break;
@@ -440,7 +442,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) {
if (FLAGS_SET(flags, XESCAPE_8_BIT))
return xescape_full(str, "", console_width, flags);
return xescape_full(str, /* bad= */ NULL, console_width, flags);
else
return utf8_escape_non_printable_full(str,
console_width,
@@ -454,6 +456,12 @@ char* octescape(const char *s, size_t len) {
assert(s || len == 0);
if (len == SIZE_MAX)
len = strlen(s);
if (len > (SIZE_MAX - 1) / 4)
return NULL;
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;

View File

@@ -13,7 +13,7 @@
#include "macro.h"
#include "string-util.h"
char *hw_addr_to_string_full(
char* hw_addr_to_string_full(
const struct hw_addr_data *addr,
HardwareAddressToStringFlags flags,
char buffer[static HW_ADDR_TO_STRING_MAX]) {

View File

@@ -36,11 +36,11 @@ typedef enum HardwareAddressToStringFlags {
} HardwareAddressToStringFlags;
#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
char *hw_addr_to_string_full(
char* hw_addr_to_string_full(
const struct hw_addr_data *addr,
HardwareAddressToStringFlags flags,
char buffer[static HW_ADDR_TO_STRING_MAX]);
static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) {
static inline char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) {
return hw_addr_to_string_full(addr, 0, buffer);
}

View File

@@ -169,7 +169,10 @@ int fd_nonblock(int fd, bool nonblock) {
if (nflags == flags)
return 0;
return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
if (fcntl(fd, F_SETFL, nflags) < 0)
return -errno;
return 1;
}
int stdio_disable_nonblock(void) {
@@ -212,9 +215,6 @@ int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
continue;
RET_GATHER(r, fd_cloexec(*fd, cloexec));
if (r >= 0)
r = 1; /* report if we did anything */
}
return r;
@@ -514,6 +514,16 @@ int pack_fds(int fds[], size_t n_fds) {
return 0;
}
int fd_validate(int fd) {
if (fd < 0)
return -EBADF;
if (fcntl(fd, F_GETFD) < 0)
return -errno;
return 0;
}
int same_fd(int a, int b) {
struct stat sta, stb;
pid_t pid;
@@ -523,25 +533,57 @@ int same_fd(int a, int b) {
assert(b >= 0);
/* Compares two file descriptors. Note that semantics are quite different depending on whether we
* have kcmp() or we don't. If we have kcmp() this will only return true for dup()ed file
* descriptors, but not otherwise. If we don't have kcmp() this will also return true for two fds of
* the same file, created by separate open() calls. Since we use this call mostly for filtering out
* duplicates in the fd store this difference hopefully doesn't matter too much. */
* have F_DUPFD_QUERY/kcmp() or we don't. If we have F_DUPFD_QUERY/kcmp() this will only return true
* for dup()ed file descriptors, but not otherwise. If we don't have F_DUPFD_QUERY/kcmp() this will
* also return true for two fds of the same file, created by separate open() calls. Since we use this
* call mostly for filtering out duplicates in the fd store this difference hopefully doesn't matter
* too much.
*
* Guarantees that if either of the passed fds is not allocated we'll return -EBADF. */
if (a == b) {
/* Let's validate that the fd is valid */
r = fd_validate(a);
if (r < 0)
return r;
if (a == b)
return true;
}
/* Try to use F_DUPFD_QUERY if we have it first, as it is the nicest API */
r = fcntl(a, F_DUPFD_QUERY, b);
if (r > 0)
return true;
if (r == 0) {
/* The kernel will return 0 in case the first fd is allocated, but the 2nd is not. (Which is different in the kcmp() case) Explicitly validate it hence. */
r = fd_validate(b);
if (r < 0)
return r;
return false;
}
/* On old kernels (< 6.10) that do not support F_DUPFD_QUERY this will return EINVAL for regular fds, and EBADF on O_PATH fds. Confusing. */
if (errno == EBADF) {
/* EBADF could mean two things: the first fd is not valid, or it is valid and is O_PATH and
* F_DUPFD_QUERY is not supported. Let's validate the fd explicitly, to distinguish this
* case. */
r = fd_validate(a);
if (r < 0)
return r;
/* If the fd is valid, but we got EBADF, then let's try kcmp(). */
} else if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno) && errno != EINVAL)
return -errno;
/* Try to use kcmp() if we have it. */
pid = getpid_cached();
r = kcmp(pid, pid, KCMP_FILE, a, b);
if (r == 0)
return true;
if (r > 0)
return false;
if (r >= 0)
return !r;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
/* We don't have kcmp(), use fstat() instead. */
/* We have neither F_DUPFD_QUERY nor kcmp(), use fstat() instead. */
if (fstat(a, &sta) < 0)
return -errno;
@@ -572,14 +614,21 @@ int same_fd(int a, int b) {
#endif /* NM_IGNORED */
void cmsg_close_all(struct msghdr *mh) {
struct cmsghdr *cmsg;
assert(mh);
CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
struct cmsghdr *cmsg;
CMSG_FOREACH(cmsg, mh) {
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
if (cmsg->cmsg_type == SCM_RIGHTS)
close_many(CMSG_TYPED_DATA(cmsg, int),
(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
else if (cmsg->cmsg_type == SCM_PIDFD) {
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
safe_close(*CMSG_TYPED_DATA(cmsg, int));
}
}
}
bool fdname_is_valid(const char *s) {
@@ -609,7 +658,6 @@ bool fdname_is_valid(const char *s) {
return p - s <= FDNAME_MAX;
}
#if 0 /* NM_IGNORED */
int fd_get_path(int fd, char **ret) {
int r;
@@ -619,19 +667,12 @@ int fd_get_path(int fd, char **ret) {
return safe_getcwd(ret);
r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
if (r == -ENOENT) {
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
* things debuggable and distinguish the two. */
if (proc_mounted() == 0)
return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot
* environment. */
return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
}
if (r == -ENOENT)
return proc_fd_enoent_errno();
return r;
}
#if 0 /* NM_IGNORED */
int move_fd(int from, int to, int cloexec) {
int r;
@@ -768,8 +809,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
}
/* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 3; i++)
if (fd[i] < 0)
fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */
else if (fd[i] != i && fd[i] < 3) {
@@ -782,20 +822,16 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
fd[i] = copy_fd[i];
}
}
/* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that
* we have freedom to move them around. If the fds already were at the right places then the specific
* fds are -EBADF. Let's now move them to the right places. This is the point of no return. */
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 3; i++)
if (fd[i] == i) {
/* fd is already in place, but let's make sure O_CLOEXEC is off */
r = fd_cloexec(i, false);
if (r < 0)
goto finish;
} else {
assert(fd[i] > 2);
@@ -804,7 +840,6 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
goto finish;
}
}
}
r = 0;
@@ -828,8 +863,6 @@ finish:
#endif /* NM_IGNORED */
int fd_reopen(int fd, int flags) {
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert(!FLAGS_SET(flags, O_CREAT));
@@ -865,13 +898,47 @@ int fd_reopen(int fd, int flags) {
if (errno != ENOENT)
return -errno;
r = proc_mounted();
if (r == 0)
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return proc_fd_enoent_errno();
}
return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is
* not valid, otherwise let's propagate the original
* error */
return new_fd;
}
int fd_reopen_propagate_append_and_position(int fd, int flags) {
/* Invokes fd_reopen(fd, flags), but propagates O_APPEND if set on original fd, and also tries to
* keep current file position.
*
* You should use this if the original fd potentially is O_APPEND, otherwise we get rather
* "unexpected" behavior. Unless you intentionally want to overwrite pre-existing data, and have
* your output overwritten by the next user.
*
* Use case: "systemd-run --pty >> some-log".
*
* The "keep position" part is obviously nonsense for the O_APPEND case, but should reduce surprises
* if someone carefully pre-positioned the passed in original input or non-append output FDs. */
assert(fd >= 0);
assert(!(flags & (O_APPEND|O_DIRECTORY)));
int existing_flags = fcntl(fd, F_GETFL);
if (existing_flags < 0)
return -errno;
int new_fd = fd_reopen(fd, flags | (existing_flags & O_APPEND));
if (new_fd < 0)
return new_fd;
/* Try to adjust the offset, but ignore errors. */
off_t p = lseek(fd, 0, SEEK_CUR);
if (p > 0) {
off_t new_p = lseek(new_fd, p, SEEK_SET);
if (new_p < 0)
log_debug_errno(errno,
"Failed to propagate file position for re-opened fd %d, ignoring: %m",
fd);
else if (new_p != p)
log_debug("Failed to propagate file position for re-opened fd %d (%lld != %lld), ignoring.",
fd, (long long) new_p, (long long) p);
}
return new_fd;
@@ -922,21 +989,21 @@ int fd_is_opath(int fd) {
return FLAGS_SET(r, O_PATH);
}
int fd_verify_safe_flags(int fd) {
int fd_verify_safe_flags_full(int fd, int extra_flags) {
int flags, unexpected_flags;
/* Check if an extrinsic fd is safe to work on (by a privileged service). This ensures that clients
* can't trick a privileged service into giving access to a file the client doesn't already have
* access to (especially via something like O_PATH).
*
* O_NOFOLLOW: For some reason the kernel will return this flag from fcntl; it doesn't go away
* O_NOFOLLOW: For some reason the kernel will return this flag from fcntl(); it doesn't go away
* immediately after open(). It should have no effect whatsoever to an already-opened FD,
* and since we refuse O_PATH it should be safe.
*
* RAW_O_LARGEFILE: glibc secretly sets this and neglects to hide it from us if we call fcntl.
* See comment in missing_fcntl.h for more details about this.
*
* O_DIRECTORY: this is set for directories, which are totally fine
* If 'extra_flags' is specified as non-zero the included flags are also allowed.
*/
assert(fd >= 0);
@@ -945,13 +1012,13 @@ int fd_verify_safe_flags(int fd) {
if (flags < 0)
return -errno;
unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|O_DIRECTORY);
unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o",
(unsigned) unexpected_flags);
return 0;
return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
}
int read_nr_open(void) {
@@ -1055,8 +1122,6 @@ int fds_are_same_mount(int fd1, int fd2) {
int mntid;
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);
@@ -1069,8 +1134,6 @@ int fds_are_same_mount(int fd1, int fd2) {
int mntid;
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);
@@ -1082,7 +1145,7 @@ int fds_are_same_mount(int fd1, int fd2) {
return statx_mount_same(&st1.nsx, &st2.nsx);
}
const char *accmode_to_string(int flags) {
const char* accmode_to_string(int flags) {
switch (flags & O_ACCMODE) {
case O_RDONLY:
return "ro";
@@ -1095,7 +1158,7 @@ const char *accmode_to_string(int flags) {
}
}
char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) {
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);
@@ -1103,3 +1166,20 @@ char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid,
return buf;
}
#endif /* NM_IGNORED */
int proc_fd_enoent_errno(void) {
int r;
/* When ENOENT is returned during the use of FORMAT_PROC_FD_PATH, it can mean two things:
* that the fd does not exist or that /proc/ is not mounted.
* Let's make things debuggable and figure out the most appropriate errno. */
r = proc_mounted();
if (r == 0)
return -ENOSYS; /* /proc/ is not available or not set up properly, we're most likely
in some chroot environment. */
if (r > 0)
return -EBADF; /* If /proc/ is definitely around then this means the fd is not valid. */
return -ENOENT; /* Otherwise let's propagate the original ENOENT. */
}

View File

@@ -80,6 +80,7 @@ int close_all_fds_without_malloc(const int except[], size_t n_except);
int pack_fds(int fds[], size_t n);
int fd_validate(int fd);
int same_fd(int a, int b);
void cmsg_close_all(struct msghdr *mh);
@@ -111,10 +112,15 @@ static inline int make_null_stdio(void) {
})
int fd_reopen(int fd, int flags);
int fd_reopen_propagate_append_and_position(int fd, int flags);
int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
int fd_is_opath(int fd);
int fd_verify_safe_flags(int fd);
int fd_verify_safe_flags_full(int fd, int extra_flags);
static inline int fd_verify_safe_flags(int fd) {
return fd_verify_safe_flags_full(fd, 0);
}
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
@@ -136,7 +142,7 @@ int fds_are_same_mount(int fd1, int fd2);
#define PROC_FD_PATH_MAX \
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
static inline char* format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
assert(buf);
assert(fd >= 0);
assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
@@ -150,13 +156,15 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f
#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);
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);
int proc_fd_enoent_errno(void);
const char* accmode_to_string(int flags);
/* Like ASSERT_PTR, but for fds */
#define ASSERT_FD(fd) \

View File

@@ -16,10 +16,12 @@
#include "alloc-util.h"
#include "chase.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "label.h"
#include "log.h"
#include "macro.h"
#include "mkdir.h"
@@ -34,7 +36,7 @@
#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)
#define READ_FULL_BYTES_MAX (64U * U64_MB - UINT64_C(1))
/* 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
@@ -47,7 +49,7 @@
* exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/
* allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we
* can detect EOFs. */
#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U)
#define READ_VIRTUAL_BYTES_MAX (4U * U64_MB - UINT64_C(2))
int fdopen_unlocked(int fd, const char *options, FILE **ret) {
assert(ret);
@@ -121,7 +123,7 @@ FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode) {
}
#if 0 /* NM_IGNORED */
int write_string_stream_ts(
int write_string_stream_full(
FILE *f,
const char *line,
WriteStringFileFlags flags,
@@ -233,23 +235,41 @@ static int write_string_file_atomic_at(
/* Note that we'd really like to use O_TMPFILE here, but can't really, since we want replacement
* semantics here, and O_TMPFILE can't offer that. i.e. rename() replaces but linkat() doesn't. */
r = fopen_temporary_at(dir_fd, fn, &f, &p);
mode_t mode = write_string_file_flags_to_mode(flags);
bool call_label_ops_post = false;
if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL)) {
r = label_ops_pre(dir_fd, fn, mode);
if (r < 0)
return r;
r = write_string_stream_ts(f, line, flags, ts);
call_label_ops_post = true;
}
r = fopen_temporary_at(dir_fd, fn, &f, &p);
if (r < 0)
goto fail;
r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
if (r < 0)
goto fail;
if (call_label_ops_post) {
call_label_ops_post = false;
if (renameat(dir_fd, p, dir_fd, fn) < 0) {
r = -errno;
r = label_ops_post(fileno(f), /* path= */ NULL, /* created= */ true);
if (r < 0)
goto fail;
}
r = write_string_stream_full(f, line, flags, ts);
if (r < 0)
goto fail;
r = fchmod_umask(fileno(f), mode);
if (r < 0)
goto fail;
r = RET_NERRNO(renameat(dir_fd, p, dir_fd, fn));
if (r < 0)
goto fail;
if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) {
/* Sync the rename, too */
r = fsync_directory_of_file(fileno(f));
@@ -260,20 +280,26 @@ static int write_string_file_atomic_at(
return 0;
fail:
if (call_label_ops_post)
(void) label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f);
if (f)
(void) unlinkat(dir_fd, p, 0);
return r;
}
int write_string_file_ts_at(
int write_string_file_full(
int dir_fd,
const char *fn,
const char *line,
WriteStringFileFlags flags,
const struct timespec *ts) {
const struct timespec *ts,
const char *label_fn) {
bool call_label_ops_post = false, made_file = false;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -EBADF;
int q, r;
int r;
assert(fn);
assert(line);
@@ -295,18 +321,37 @@ int write_string_file_ts_at(
goto fail;
return r;
} else
assert(!ts);
}
mode_t mode = write_string_file_flags_to_mode(flags);
if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
if (r < 0)
goto fail;
call_label_ops_post = true;
}
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
fd = openat(dir_fd, fn, O_CLOEXEC|O_NOCTTY |
fd = openat_report_new(
dir_fd, fn, O_CLOEXEC | O_NOCTTY |
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
write_string_file_flags_to_mode(flags));
mode,
&made_file);
if (fd < 0) {
r = -errno;
r = fd;
goto fail;
}
if (call_label_ops_post) {
call_label_ops_post = false;
r = label_ops_post(fd, /* path= */ NULL, made_file);
if (r < 0)
goto fail;
}
@@ -317,26 +362,31 @@ int write_string_file_ts_at(
if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
setvbuf(f, NULL, _IONBF, 0);
r = write_string_stream_ts(f, line, flags, ts);
r = write_string_stream_full(f, line, flags, ts);
if (r < 0)
goto fail;
return 0;
fail:
if (call_label_ops_post)
(void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file);
if (made_file)
(void) unlinkat(dir_fd, fn, 0);
if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
return r;
f = safe_fclose(f);
fd = safe_close(fd);
/* OK, the operation failed, but let's see if the right
* contents in place already. If so, eat up the error. */
q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE));
if (q <= 0)
return r;
/* OK, the operation failed, but let's see if the right contents in place already. If so, eat up the
* error. */
if (verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
return 0;
return r;
}
int write_string_filef(
@@ -358,6 +408,22 @@ int write_string_filef(
return write_string_file(fn, p, flags);
}
int write_base64_file_at(
int dir_fd,
const char *fn,
const struct iovec *data,
WriteStringFileFlags flags) {
_cleanup_free_ char *encoded = NULL;
ssize_t n;
n = base64mem_full(data ? data->iov_base : NULL, data ? data->iov_len : 0, 79, &encoded);
if (n < 0)
return n;
return write_string_file_at(dir_fd, fn, encoded, flags);
}
int read_one_line_file_at(int dir_fd, const char *filename, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -796,35 +862,49 @@ int read_full_file_full(
}
#if 0 /* NM_IGNORED */
int executable_is_script(const char *path, char **interpreter) {
_cleanup_free_ char *line = NULL;
size_t len;
char *ans;
int script_get_shebang_interpreter(const char *path, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(path);
r = read_one_line_file(path, &line);
if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
return 0;
f = fopen(path, "re");
if (!f)
return -errno;
char c;
r = safe_fgetc(f, &c);
if (r < 0)
return r;
if (r == 0)
return -EBADMSG;
if (c != '#')
return -EMEDIUMTYPE;
r = safe_fgetc(f, &c);
if (r < 0)
return r;
if (r == 0)
return -EBADMSG;
if (c != '!')
return -EMEDIUMTYPE;
_cleanup_free_ char *line = NULL;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (!startswith(line, "#!"))
_cleanup_free_ char *p = NULL;
const char *s = line;
r = extract_first_word(&s, &p, /* separators = */ NULL, /* flags = */ 0);
if (r < 0)
return r;
if (r == 0)
return -ENOEXEC;
if (ret)
*ret = TAKE_PTR(p);
return 0;
ans = strstrip(line + 2);
len = strcspn(ans, " \t");
if (len == 0)
return 0;
ans = strndup(ans, len);
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
}
/**
@@ -897,19 +977,21 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
return 0;
}
DIR *xopendirat(int fd, const char *name, int flags) {
_cleanup_close_ int nfd = -EBADF;
DIR* xopendirat(int dir_fd, const char *name, int flags) {
_cleanup_close_ int fd = -EBADF;
assert(!(flags & O_CREAT));
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(name);
assert(!(flags & (O_CREAT|O_TMPFILE)));
if (fd == AT_FDCWD && flags == 0)
if (dir_fd == AT_FDCWD && flags == 0)
return opendir(name);
nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
if (nfd < 0)
fd = openat(dir_fd, name, O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags);
if (fd < 0)
return NULL;
return take_fdopendir(&nfd);
return take_fdopendir(&fd);
}
#endif /* NM_IGNORED */
@@ -1364,6 +1446,29 @@ int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *sp
return 0;
}
int fputs_with_newline(FILE *f, const char *s) {
/* This is like fputs() but outputs a trailing newline char, but only if the string isn't empty
* and doesn't end in a newline already. Returns 0 in case we didn't append a newline, > 0 otherwise. */
if (isempty(s))
return 0;
if (!f)
f = stdout;
if (fputs(s, f) < 0)
return -EIO;
if (endswith(s, "\n"))
return 0;
if (fputc('\n', f) < 0)
return -EIO;
return 1;
}
#if 0 /* NM_IGNORED */
/* A bitmask of the EOL markers we know */
typedef enum EndOfLineMarker {
@@ -1515,7 +1620,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
int read_stripped_line(FILE *f, size_t limit, char **ret) {
_cleanup_free_ char *s = NULL;
int r;
int r, k;
assert(f);
@@ -1524,23 +1629,17 @@ int read_stripped_line(FILE *f, size_t limit, char **ret) {
return r;
if (ret) {
const char *p;
p = strstrip(s);
const char *p = strstrip(s);
if (p == s)
*ret = TAKE_PTR(s);
else {
char *copy;
copy = strdup(p);
if (!copy)
return -ENOMEM;
*ret = copy;
k = strdup_to(ret, p);
if (k < 0)
return k;
}
}
return r;
return r > 0; /* Return 1 if something was read. */
}
int safe_fgetc(FILE *f, char *ret) {

View File

@@ -28,11 +28,7 @@ typedef enum {
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
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()
and friends. */
WRITE_STRING_FILE_LABEL = 1 << 13,
} WriteStringFileFlags;
typedef enum {
@@ -51,23 +47,22 @@ DIR* take_fdopendir(int *dfd);
FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc);
FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode);
int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
int write_string_stream_full(FILE *f, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
return write_string_stream_ts(f, line, flags, NULL);
}
int write_string_file_ts_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
static inline int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts) {
return write_string_file_ts_at(AT_FDCWD, fn, line, flags, ts);
}
static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_ts_at(dir_fd, fn, line, flags, NULL);
}
static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_ts(fn, line, flags, NULL);
return write_string_stream_full(f, line, flags, NULL);
}
int write_string_file_full(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts, const char *label_fn);
static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_full(dir_fd, fn, line, flags, NULL, NULL);
}
static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_at(AT_FDCWD, fn, line, flags);
}
int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
int write_base64_file_at(int dir_fd, const char *fn, const struct iovec *data, WriteStringFileFlags flags);
int read_one_line_file_at(int dir_fd, const char *filename, char **ret);
static inline int read_one_line_file(const char *filename, char **ret) {
return read_one_line_file_at(AT_FDCWD, filename, ret);
@@ -99,11 +94,11 @@ static inline int verify_file(const char *fn, const char *blob, bool accept_extr
return verify_file_at(AT_FDCWD, fn, blob, accept_extra_nl);
}
int executable_is_script(const char *path, char **interpreter);
int script_get_shebang_interpreter(const char *path, char **ret);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
DIR *xopendirat(int dirfd, const char *name, int flags);
DIR* xopendirat(int dir_fd, const char *name, int flags);
typedef enum XfopenFlags {
XFOPEN_UNLOCKED = 1 << 0, /* call __fsetlocking(FSETLOCKING_BYCALLER) after opened */
@@ -144,6 +139,7 @@ int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space);
int fputs_with_newline(FILE *f, const char *s);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
@@ -152,6 +148,14 @@ typedef enum ReadLineFlags {
} ReadLineFlags;
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
static inline int read_line(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, 0, ret);
}
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);
static inline bool file_offset_beyond_memory_size(off_t x) {
if (x < 0) /* off_t is signed, filter that out */
@@ -159,16 +163,6 @@ static inline bool file_offset_beyond_memory_size(off_t x) {
return (uint64_t) x > (uint64_t) SIZE_MAX;
}
static inline int read_line(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, 0, ret);
}
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

@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-shared.h"
#include "format-ifname.h"
#include "string-util.h"
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
if (ifindex <= 0)
return -EINVAL;
if (if_indextoname(ifindex, buf))
return 0;
if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
return -errno;
if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
assert_se(snprintf_ok(buf, IF_NAMESIZE, "%%%d", ifindex));
else
assert_se(snprintf_ok(buf, IF_NAMESIZE, "%d", ifindex));
return 0;
}
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
char buf[IF_NAMESIZE];
int r;
assert(ret);
r = format_ifname_full(ifindex, flag, buf);
if (r < 0)
return r;
return strdup_to(ret, buf);
}

View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <net/if.h>
typedef enum {
FORMAT_IFNAME_IFINDEX = 1 << 0,
FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
} FormatIfnameFlag;
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]);
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret);
static inline int format_ifname(int ifindex, char buf[static IF_NAMESIZE]) {
return format_ifname_full(ifindex, 0, buf);
}
static inline int format_ifname_alloc(int ifindex, char **ret) {
return format_ifname_full_alloc(ifindex, 0, ret);
}
static inline char* _format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
(void) format_ifname_full(ifindex, flag, buf);
return buf;
}
#define FORMAT_IFNAME_FULL(index, flag) _format_ifname_full(index, flag, (char[IF_NAMESIZE]){})
#define FORMAT_IFNAME(index) _format_ifname_full(index, 0, (char[IF_NAMESIZE]){})

View File

@@ -7,44 +7,7 @@
#include "stdio-util.h"
#include "strxcpyx.h"
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
if (ifindex <= 0)
return -EINVAL;
if (if_indextoname(ifindex, buf))
return 0;
if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
return -errno;
if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
assert(snprintf_ok(buf, IF_NAMESIZE, "%%%d", ifindex));
else
assert(snprintf_ok(buf, IF_NAMESIZE, "%d", ifindex));
return 0;
}
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
char buf[IF_NAMESIZE], *copy;
int r;
assert(ret);
r = format_ifname_full(ifindex, flag, buf);
if (r < 0)
return r;
copy = strdup(buf);
if (!copy)
return -ENOMEM;
*ret = copy;
return 0;
}
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
char* format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
typedef struct {
const char *suffix;
uint64_t factor;
@@ -77,15 +40,17 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
for (size_t i = 0; i < n; i++)
if (t >= table[i].factor) {
if (flag & FORMAT_BYTES_BELOW_POINT) {
uint64_t remainder = i != n - 1 ?
(t / table[i + 1].factor * 10 / table[n - 1].factor) % 10 :
(t * 10 / table[i].factor) % 10;
if (FLAGS_SET(flag, FORMAT_BYTES_BELOW_POINT) && remainder > 0)
(void) snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
i != n - 1 ?
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
remainder,
table[i].suffix);
} else
else
(void) snprintf(buf, l,
"%" PRIu64 "%s",
t / table[i].factor,

View File

@@ -2,7 +2,6 @@
#pragma once
#include <inttypes.h>
#include <net/if.h>
#include <stdbool.h>
#include "cgroup-util.h"
@@ -68,29 +67,6 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t));
# error Unknown ino_t size
#endif
typedef enum {
FORMAT_IFNAME_IFINDEX = 1 << 0,
FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
} FormatIfnameFlag;
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]);
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret);
static inline int format_ifname(int ifindex, char buf[static IF_NAMESIZE]) {
return format_ifname_full(ifindex, 0, buf);
}
static inline int format_ifname_alloc(int ifindex, char **ret) {
return format_ifname_full_alloc(ifindex, 0, ret);
}
static inline char *_format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
(void) format_ifname_full(ifindex, flag, buf);
return buf;
}
#define FORMAT_IFNAME_FULL(index, flag) _format_ifname_full(index, flag, (char[IF_NAMESIZE]){})
#define FORMAT_IFNAME(index) _format_ifname_full(index, 0, (char[IF_NAMESIZE]){})
typedef enum {
FORMAT_BYTES_USE_IEC = 1 << 0,
FORMAT_BYTES_BELOW_POINT = 1 << 1,
@@ -99,10 +75,10 @@ typedef enum {
#define FORMAT_BYTES_MAX 16U
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) _warn_unused_result_;
char* format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) _warn_unused_result_;
_warn_unused_result_
static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
static inline char* format_bytes(char *buf, size_t l, uint64_t t) {
return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
}

View File

@@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "btrfs.h"
#include "chattr-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -156,10 +157,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
}
}
int readlink_malloc(const char *p, char **ret) {
return readlinkat_malloc(AT_FDCWD, p, ret);
}
int readlink_value(const char *p, char **ret) {
_cleanup_free_ char *link = NULL, *name = NULL;
int r;
@@ -318,10 +315,7 @@ int fchmod_opath(int fd, mode_t m) {
if (errno != ENOENT)
return -errno;
if (proc_mounted() == 0)
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return -ENOENT;
return proc_fd_enoent_errno();
}
return 0;
@@ -330,14 +324,21 @@ int fchmod_opath(int fd, mode_t m) {
int futimens_opath(int fd, const struct timespec ts[2]) {
/* Similar to fchmod_opath() but for futimens() */
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
assert(fd >= 0);
if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
return -errno;
/* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
* if unavailable. */
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags = */ 0) < 0) {
if (errno != ENOENT)
return -errno;
if (proc_mounted() == 0)
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return -ENOENT;
return proc_fd_enoent_errno();
}
return 0;
@@ -375,9 +376,21 @@ int fd_warn_permissions(const char *path, int fd) {
return stat_warn_permissions(path, &st);
}
int touch_fd(int fd, usec_t stamp) {
assert(fd >= 0);
if (stamp == USEC_INFINITY)
return futimens_opath(fd, /* ts= */ NULL);
struct timespec ts[2];
timespec_store(ts + 0, stamp);
ts[1] = ts[0];
return futimens_opath(fd, ts);
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
_cleanup_close_ int fd = -EBADF;
int r, ret;
int ret;
assert(path);
@@ -409,21 +422,10 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
* something fchown(), fchmod(), futimensat() don't allow. */
ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
r = futimens_opath(fd, ts);
} else
r = futimens_opath(fd, NULL);
if (r < 0 && ret >= 0)
return r;
return ret;
return RET_GATHER(ret, touch_fd(fd, stamp));
}
int symlink_idempotent(const char *from, const char *to, bool make_relative) {
int symlinkat_idempotent(const char *from, int atfd, const char *to, bool make_relative) {
_cleanup_free_ char *relpath = NULL;
int r;
@@ -438,13 +440,13 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) {
from = relpath;
}
if (symlink(from, to) < 0) {
if (symlinkat(from, atfd, to) < 0) {
_cleanup_free_ char *p = NULL;
if (errno != EEXIST)
return -errno;
r = readlink_malloc(to, &p);
r = readlinkat_malloc(atfd, to, &p);
if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
return -EEXIST;
if (r < 0) /* Any other error? In that case propagate it as is */
@@ -632,11 +634,11 @@ static int tmp_dir_internal(const char *def, const char **ret) {
return 0;
}
k = is_dir(def, true);
k = is_dir(def, /* follow = */ true);
if (k == 0)
k = -ENOTDIR;
if (k < 0)
return r < 0 ? r : k;
return RET_GATHER(r, k);
*ret = def;
return 0;
@@ -644,6 +646,7 @@ static int tmp_dir_internal(const char *def, const char **ret) {
#if 0 /* NM_IGNORED */
int var_tmp_dir(const char **ret) {
assert(ret);
/* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
* even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
@@ -655,6 +658,7 @@ int var_tmp_dir(const char **ret) {
#endif /* NM_IGNORED */
int tmp_dir(const char **ret) {
assert(ret);
/* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
* backed by an in-memory file system: /tmp. */
@@ -664,6 +668,8 @@ int tmp_dir(const char **ret) {
#if 0 /* NM_IGNORED */
int unlink_or_warn(const char *filename) {
assert(filename);
if (unlink(filename) < 0 && errno != ENOENT)
/* If the file doesn't exist and the fs simply was read-only (in which
* case unlink() returns EROFS even if the file doesn't exist), don't
@@ -675,39 +681,35 @@ int unlink_or_warn(const char *filename) {
}
int access_fd(int fd, int mode) {
assert(fd >= 0);
/* Like access() but operates on an already open fd */
if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
return -errno;
/* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
* if unavailable. */
if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (errno != ENOENT)
return -errno;
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's
* make things debuggable and distinguish the two. */
if (proc_mounted() == 0)
return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot
* environment. */
return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
return proc_fd_enoent_errno();
}
return 0;
}
void unlink_tempfilep(char (*p)[]) {
/* If the file is created with mkstemp(), it will (almost always)
* change the suffix. Treat this as a sign that the file was
* successfully created. We ignore both the rare case where the
* original suffix is used and unlink failures. */
if (!endswith(*p, ".XXXXXX"))
(void) unlink(*p);
}
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
_cleanup_close_ int truncate_fd = -EBADF;
struct stat st;
off_t l, bs;
assert(fd >= 0 || fd == AT_FDCWD);
assert(name);
assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0);
/* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
@@ -1028,7 +1030,7 @@ int parse_cifs_service(
return 0;
}
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF, parent_fd = -EBADF;
_cleanup_free_ char *fname = NULL, *parent = NULL;
int r;
@@ -1064,7 +1066,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
path = fname;
}
fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, xopen_flags, mode);
if (IN_SET(fd, -ELOOP, -ENOTDIR))
return -EEXIST;
if (fd < 0)
@@ -1074,46 +1076,50 @@ 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) {
unsigned attempts = 7;
int fd;
/* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if
* it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will
* shortcut to openat() otherwise */
if (!ret_newly_created)
return RET_NERRNO(openat(dirfd, pathname, flags, mode));
* shortcut to openat() otherwise.
*
* Note that this routine is a bit more strict with symlinks than regular openat() is. If O_NOFOLLOW
* is not specified, then we'll follow the symlink when opening an existing file but we will *not*
* follow it when creating a new one (because that's a terrible UNIX misfeature and generally a
* security hole). */
if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) {
fd = openat(dirfd, pathname, flags, mode);
if (fd < 0)
return -errno;
if (ret_newly_created)
*ret_newly_created = FLAGS_SET(flags, O_CREAT);
return fd;
}
for (;;) {
for (unsigned attempts = 7;;) {
/* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
if (fd >= 0) {
if (ret_newly_created)
*ret_newly_created = false;
return fd;
}
if (errno != ENOENT)
return -errno;
/* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */
fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode);
/* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL/O_NOFOLLOW. */
fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
if (fd >= 0) {
if (ret_newly_created)
*ret_newly_created = true;
return fd;
}
if (errno != EEXIST)
return -errno;
/* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again
* without the two flags. But let's not spin forever, hence put a limit on things */
/* Hmm, so now we got EEXIST? Then someone might have created the file between the first and
* second call to openat(). Let's try again but with a limit so we don't spin forever. */
if (--attempts == 0) /* Give up eventually, somebody is playing with us */
return -EEXIST;
@@ -1122,11 +1128,14 @@ int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, b
int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF;
bool made = false;
bool made_dir = false, made_file = false;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
/* An inode cannot be both a directory and a regular file at the same time. */
assert(!(FLAGS_SET(open_flags, O_DIRECTORY) && FLAGS_SET(xopen_flags, XO_REGULAR)));
/* 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
@@ -1135,17 +1144,37 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
* • 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 XO_NOCOW is specified will turn on the NOCOW btrfs flag on the file, if available.
*
* • if XO_REGULAR is specified will return an error if inode is not a regular file.
*
* • If mode is specified as MODE_INVALID, we'll use 0755 for dirs, and 0644 for regular files.
*/
if (mode == MODE_INVALID)
mode = (open_flags & O_DIRECTORY) ? 0755 : 0644;
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
r = fd_verify_regular(dir_fd);
if (r < 0)
return r;
}
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
}
bool call_label_ops_post = false;
if (FLAGS_SET(open_flags, O_CREAT) && FLAGS_SET(xopen_flags, XO_LABEL)) {
r = label_ops_pre(dir_fd, path, FLAGS_SET(open_flags, O_DIRECTORY) ? S_IFDIR : S_IFREG);
if (r < 0)
return r;
call_label_ops_post = true;
}
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
@@ -1156,49 +1185,100 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
if (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST;
made = false;
} else if (r < 0)
return r;
else
made = true;
if (FLAGS_SET(xopen_flags, XO_LABEL)) {
r = label_ops_post(dir_fd, path);
if (r < 0)
return r;
}
made_dir = true;
open_flags &= ~(O_EXCL|O_CREAT);
xopen_flags &= ~XO_LABEL;
}
fd = RET_NERRNO(openat(dir_fd, path, open_flags, mode));
if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
/* Guarantee we return a regular fd only, and don't open the file unless we verified it
* first */
if (FLAGS_SET(open_flags, O_PATH)) {
fd = openat(dir_fd, path, open_flags, mode);
if (fd < 0) {
if (IN_SET(fd,
/* We got ENOENT? then someone else immediately removed it after we
* created it. In that case let's return immediately without unlinking
* anything, because there simply isn't anything to unlink anymore. */
-ENOENT,
/* is a symlink? exists already → created by someone else, don't unlink */
-ELOOP,
/* not a directory? exists already → created by someone else, don't unlink */
-ENOTDIR))
return fd;
if (made)
(void) unlinkat(dir_fd, path, AT_REMOVEDIR);
return fd;
r = -errno;
goto error;
}
if (FLAGS_SET(open_flags, O_CREAT) && FLAGS_SET(xopen_flags, XO_LABEL)) {
r = label_ops_post(dir_fd, path);
r = fd_verify_regular(fd);
if (r < 0)
return r;
goto error;
} else if (FLAGS_SET(open_flags, O_CREAT|O_EXCL)) {
/* In O_EXCL mode we can just create the thing, everything is dealt with for us */
fd = openat(dir_fd, path, open_flags, mode);
if (fd < 0) {
r = -errno;
goto error;
}
made_file = true;
} else {
/* Otherwise pin the inode first via O_PATH */
_cleanup_close_ int inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|(open_flags & O_NOFOLLOW));
if (inode_fd < 0) {
if (errno != ENOENT || !FLAGS_SET(open_flags, O_CREAT)) {
r = -errno;
goto error;
}
/* Doesn't exist yet, then try to create it */
fd = openat(dir_fd, path, open_flags|O_CREAT|O_EXCL, mode);
if (fd < 0) {
r = -errno;
goto error;
}
made_file = true;
} else {
/* OK, we pinned it. Now verify it's actually a regular file, and then reopen it */
r = fd_verify_regular(inode_fd);
if (r < 0)
goto error;
fd = fd_reopen(inode_fd, open_flags & ~(O_NOFOLLOW|O_CREAT));
if (fd < 0) {
r = fd;
goto error;
}
}
}
} else {
fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
if (fd < 0) {
r = fd;
goto error;
}
}
if (call_label_ops_post) {
call_label_ops_post = false;
r = label_ops_post(fd, /* path= */ NULL, made_file || made_dir);
if (r < 0)
goto error;
}
if (FLAGS_SET(xopen_flags, XO_NOCOW)) {
r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
goto error;
}
return TAKE_FD(fd);
error:
if (call_label_ops_post)
(void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : path, made_dir || made_file);
if (made_dir || made_file)
(void) unlinkat(dir_fd, path, made_dir ? AT_REMOVEDIR : 0);
return r;
}
#if 0 /* NM_IGNORED */
@@ -1247,4 +1327,103 @@ int xopenat_lock_full(
return TAKE_FD(fd);
}
int link_fd(int fd, int newdirfd, const char *newpath) {
int r, k;
assert(fd >= 0);
assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
assert(newpath);
/* Try linking via /proc/self/fd/ first. */
r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
if (r != -ENOENT)
return r;
/* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a
* more recent kernel, but does not require /proc/ mounted) */
k = proc_mounted();
if (k < 0)
return r;
if (k > 0)
return -EBADF;
return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
}
int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
_cleanup_close_ int old_fd = -EBADF;
int r;
assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
assert(!isempty(newpath)); /* source path is optional, but the target path is not */
/* Like linkat() but replaces the target if needed. Is a NOP if source and target already share the
* same inode. */
if (olddirfd == AT_FDCWD && isempty(oldpath)) /* Refuse operating on the cwd (which is a dir, and dirs can't be hardlinked) */
return -EISDIR;
if (path_implies_directory(oldpath)) /* Refuse these definite directories early */
return -EISDIR;
if (path_implies_directory(newpath))
return -EISDIR;
/* First, try to link this directly */
if (oldpath)
r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, newpath, 0));
else
r = link_fd(olddirfd, newdirfd, newpath);
if (r >= 0)
return 0;
if (r != -EEXIST)
return r;
old_fd = xopenat(olddirfd, oldpath, O_PATH|O_CLOEXEC);
if (old_fd < 0)
return old_fd;
struct stat old_st;
if (fstat(old_fd, &old_st) < 0)
return -errno;
if (S_ISDIR(old_st.st_mode)) /* Don't bother if we are operating on a directory */
return -EISDIR;
struct stat new_st;
if (fstatat(newdirfd, newpath, &new_st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
if (S_ISDIR(new_st.st_mode)) /* Refuse replacing directories */
return -EEXIST;
if (stat_inode_same(&old_st, &new_st)) /* Already the same inode? Then shortcut this */
return 0;
_cleanup_free_ char *tmp_path = NULL;
r = tempfn_random(newpath, /* extra= */ NULL, &tmp_path);
if (r < 0)
return r;
r = link_fd(old_fd, newdirfd, tmp_path);
if (r < 0) {
if (!ERRNO_IS_PRIVILEGE(r))
return r;
/* If that didn't work due to permissions then go via the path of the dentry */
r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, tmp_path, 0));
if (r < 0)
return r;
}
r = RET_NERRNO(renameat(newdirfd, tmp_path, newdirfd, newpath));
if (r < 0) {
(void) unlinkat(newdirfd, tmp_path, /* flags= */ 0);
return r;
}
return 0;
}
#endif /* NM_IGNORED */

View File

@@ -28,9 +28,11 @@ int rmdir_parents(const char *path, const char *stop);
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
static inline int readlink_malloc(const char *p, char **ret) {
return readlinkat_malloc(AT_FDCWD, p, ret);
}
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
int readlink_and_make_absolute(const char *p, char **ret);
int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid);
static inline int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
@@ -49,16 +51,21 @@ int futimens_opath(int fd, const struct timespec ts[2]);
int fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st);
#define laccess(path, mode) \
#define access_nofollow(path, mode) \
RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW))
int touch_fd(int fd, usec_t stamp);
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
static inline int touch(const char *path) {
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
}
int symlink_idempotent(const char *from, const char *to, bool make_relative);
int symlinkat_idempotent(const char *from, int atfd, const char *to, bool make_relative);
static inline int symlink_idempotent(const char *from, const char *to, bool make_relative) {
return symlinkat_idempotent(from, AT_FDCWD, to, make_relative);
}
int symlinkat_atomic_full(const char *from, int atfd, const char *to, bool make_relative);
static inline int symlink_atomic(const char *from, const char *to) {
@@ -105,8 +112,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
int access_fd(int fd, int mode);
void unlink_tempfilep(char (*p)[]);
typedef enum UnlinkDeallocateFlags {
UNLINK_REMOVEDIR = 1 << 0,
UNLINK_ERASE = 1 << 1,
@@ -128,15 +133,23 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
typedef enum XOpenFlags {
XO_LABEL = 1 << 0, /* When creating: relabel */
XO_SUBVOLUME = 1 << 1, /* When creating as directory: make it a subvolume */
XO_NOCOW = 1 << 2, /* Enable NOCOW mode after opening */
XO_REGULAR = 1 << 3, /* Fail if the inode is not a regular file */
} XOpenFlags;
int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode);
static inline int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
return open_mkdir_at_full(dirfd, path, flags, 0, mode);
}
static inline int open_mkdir(const char *path, int flags, mode_t mode) {
return open_mkdir_at_full(AT_FDCWD, path, flags, 0, 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_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
static inline int xopenat(int dir_fd, const char *path, int open_flags) {
return xopenat_full(dir_fd, path, open_flags, 0, 0);
@@ -146,3 +159,16 @@ int xopenat_lock_full(int dir_fd, const char *path, int open_flags, XOpenFlags x
static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, LockType locktype, int operation) {
return xopenat_lock_full(dir_fd, path, open_flags, 0, 0, locktype, operation);
}
int link_fd(int fd, int newdirfd, const char *newpath);
int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
static inline int at_flags_normalize_nofollow(int flags) {
if (FLAGS_SET(flags, AT_SYMLINK_FOLLOW)) {
assert(!FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW));
flags &= ~AT_SYMLINK_FOLLOW;
} else
flags |= AT_SYMLINK_NOFOLLOW;
return flags;
}

View File

@@ -25,7 +25,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled;
}
const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
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
@@ -82,6 +82,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S",
[SPECIAL_GLYPH_IDCARD] = "@",
},
/* UTF-8 */
@@ -151,6 +153,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
[SPECIAL_GLYPH_IDCARD] = u8"🪪",
},
};

View File

@@ -55,22 +55,24 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_SUPERHERO,
SPECIAL_GLYPH_IDCARD,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
bool emoji_enabled(void);
const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
const char* special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
static inline const char *special_glyph(SpecialGlyph code) {
static inline const char* special_glyph(SpecialGlyph code) {
return special_glyph_full(code, false);
}
static inline const char *special_glyph_check_mark(bool b) {
static inline const char* special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}
static inline const char *special_glyph_check_mark_space(bool b) {
static inline const char* special_glyph_check_mark_space(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
}

View File

@@ -878,6 +878,26 @@ int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_
return ordered_hashmap_put(*h, key, value);
}
int _ordered_hashmap_ensure_replace(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) {
int r;
r = _ordered_hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return ordered_hashmap_replace(*h, key, value);
}
int _hashmap_ensure_replace(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) {
int r;
r = _hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return hashmap_replace(*h, key, value);
}
static void hashmap_free_no_clear(HashmapBase *h) {
assert(!h->has_indirect);
assert(h->n_direct_entries == 0);

View File

@@ -130,14 +130,19 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS);
int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
int _hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);
int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
int _hashmap_ensure_replace(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);
#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_ensure_put(s, ops, key, value) _hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_ensure_replace(s, ops, key, value) _hashmap_ensure_replace(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS)
int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);
#define ordered_hashmap_ensure_put(s, ops, key, value) _ordered_hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS)
int _ordered_hashmap_ensure_replace(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);
#define ordered_hashmap_ensure_replace(s, ops, key, value) _ordered_hashmap_ensure_replace(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS)
IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h);
static inline IteratedCache* hashmap_iterated_cache_new(Hashmap *h) {
return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));

View File

@@ -38,7 +38,7 @@ int undecchar(char c) {
}
char hexchar(int x) {
static const char table[16] = "0123456789abcdef";
static const char table[] = "0123456789abcdef";
return table[x & 15];
}
@@ -57,7 +57,7 @@ int unhexchar(char c) {
return -EINVAL;
}
char *hexmem(const void *p, size_t l) {
char* hexmem(const void *p, size_t l) {
const uint8_t *x;
char *r, *z;
@@ -171,7 +171,7 @@ int unhexmem_full(
* useful when representing NSEC3 hashes, as one can then verify the
* order of hashes directly from their representation. */
char base32hexchar(int x) {
static const char table[32] = "0123456789"
static const char table[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUV";
return table[x & 31];
@@ -191,7 +191,7 @@ int unbase32hexchar(char c) {
return -EINVAL;
}
char *base32hexmem(const void *p, size_t l, bool padding) {
char* base32hexmem(const void *p, size_t l, bool padding) {
char *r, *z;
const uint8_t *x;
size_t len;
@@ -522,7 +522,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
/* https://tools.ietf.org/html/rfc4648#section-4 */
char base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
return table[x & 63];
@@ -533,7 +533,7 @@ char base64char(int x) {
* since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames).
* See section #5 of RFC 4648. */
char urlsafe_base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
return table[x & 63];
@@ -873,6 +873,9 @@ void hexdump(FILE *f, const void *p, size_t s) {
assert(b || s == 0);
if (s == SIZE_MAX)
s = strlen(p);
if (!f)
f = stdout;

View File

@@ -17,7 +17,7 @@ int undecchar(char c) _const_;
char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char *hexmem(const void *p, size_t l);
char* hexmem(const void *p, size_t l);
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);
@@ -30,7 +30,7 @@ char base64char(int x) _const_;
char urlsafe_base64char(int x) _const_;
int unbase64char(char c) _const_;
char *base32hexmem(const void *p, size_t l, bool padding);
char* base32hexmem(const void *p, size_t l, bool padding);
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);

View File

@@ -197,6 +197,52 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
}
#if 0 /* NM_IGNORED */
bool in4_addr_prefix_intersect(
const struct in_addr *a,
unsigned aprefixlen,
const struct in_addr *b,
unsigned bprefixlen) {
assert(a);
assert(b);
unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in_addr) * 8));
if (m == 0)
return true; /* Let's return earlier, to avoid shift by 32. */
uint32_t x = be32toh(a->s_addr ^ b->s_addr);
uint32_t n = 0xFFFFFFFFUL << (32 - m);
return (x & n) == 0;
}
bool in6_addr_prefix_intersect(
const struct in6_addr *a,
unsigned aprefixlen,
const struct in6_addr *b,
unsigned bprefixlen) {
assert(a);
assert(b);
unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in6_addr) * 8));
if (m == 0)
return true;
for (size_t i = 0; i < sizeof(struct in6_addr); i++) {
uint8_t x = a->s6_addr[i] ^ b->s6_addr[i];
uint8_t n = m < 8 ? (0xFF << (8 - m)) : 0xFF;
if ((x & n) != 0)
return false;
if (m <= 8)
break;
m -= 8;
}
return true;
}
int in_addr_prefix_intersect(
int family,
const union in_addr_union *a,
@@ -204,51 +250,16 @@ int in_addr_prefix_intersect(
const union in_addr_union *b,
unsigned bprefixlen) {
unsigned m;
assert(a);
assert(b);
/* Checks whether there are any addresses that are in both networks */
/* Checks whether there are any addresses that are in both networks. */
m = MIN(aprefixlen, bprefixlen);
if (family == AF_INET)
return in4_addr_prefix_intersect(&a->in, aprefixlen, &b->in, bprefixlen);
if (family == AF_INET) {
uint32_t x, nm;
x = be32toh(a->in.s_addr ^ b->in.s_addr);
nm = m == 0 ? 0 : 0xFFFFFFFFUL << (32 - m);
return (x & nm) == 0;
}
if (family == AF_INET6) {
unsigned i;
if (m > 128)
m = 128;
for (i = 0; i < 16; i++) {
uint8_t x, nm;
x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
if (m < 8)
nm = 0xFF << (8 - m);
else
nm = 0xFF;
if ((x & nm) != 0)
return 0;
if (m > 8)
m -= 8;
else
m = 0;
}
return 1;
}
if (family == AF_INET6)
return in6_addr_prefix_intersect(&a->in6, aprefixlen, &b->in6, bprefixlen);
return -EAFNOSUPPORT;
}
@@ -888,7 +899,7 @@ int in_addr_prefix_from_string(
return 0;
}
int in_addr_prefix_from_string_auto_internal(
int in_addr_prefix_from_string_auto_full(
const char *p,
InAddrPrefixLenMode mode,
int *ret_family,

View File

@@ -61,7 +61,22 @@ bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a);
bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
bool in4_addr_prefix_intersect(
const struct in_addr *a,
unsigned aprefixlen,
const struct in_addr *b,
unsigned bprefixlen);
bool in6_addr_prefix_intersect(
const struct in6_addr *a,
unsigned aprefixlen,
const struct in6_addr *b,
unsigned bprefixlen);
int in_addr_prefix_intersect(
int family,
const union in_addr_union *a,
unsigned aprefixlen,
const union in_addr_union *b,
unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth);
int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
@@ -166,9 +181,9 @@ typedef enum InAddrPrefixLenMode {
PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */
} InAddrPrefixLenMode;
int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
int in_addr_prefix_from_string_auto_full(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
static inline int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
return in_addr_prefix_from_string_auto_internal(p, PREFIXLEN_FULL, ret_family, ret_prefix, ret_prefixlen);
return in_addr_prefix_from_string_auto_full(p, PREFIXLEN_FULL, ret_family, ret_prefix, ret_prefixlen);
}
static inline size_t FAMILY_ADDRESS_SIZE(int family) {

View File

@@ -44,7 +44,10 @@ bool inotify_event_next(
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd, r;
int wd;
assert(fd >= 0);
assert(what >= 0);
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
@@ -52,14 +55,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
if (errno != ENOENT)
return -errno;
/* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */
r = proc_mounted();
if (r == 0)
return -ENOSYS;
if (r > 0)
return -EBADF;
return -ENOENT; /* OK, no clue, let's propagate the original error */
return proc_fd_enoent_errno();
}
return wd;

View File

@@ -6,25 +6,16 @@
#include <sys/uio.h>
#include "alloc-util.h"
#include "iovec-util-fundamental.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, \
}
extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */
extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. non-NULL) pointer */
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 */
@@ -41,14 +32,6 @@ static inline struct iovec* iovec_make_string(struct iovec *iovec, const 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);
@@ -56,16 +39,6 @@ static inline void iovec_done_erase(struct iovec *iovec) {
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);
@@ -97,3 +70,5 @@ static inline struct iovec *iovec_memdup(const struct iovec *source, struct iove
return ret;
}
struct iovec* iovec_append(struct iovec *iovec, const struct iovec *append);

View File

@@ -6,10 +6,13 @@
#include <stddef.h>
#include "label.h"
#include "macro.h"
static const LabelOps *label_ops = NULL;
int label_ops_set(const LabelOps *ops) {
assert(ops);
if (label_ops)
return -EBUSY;
@@ -17,6 +20,10 @@ int label_ops_set(const LabelOps *ops) {
return 0;
}
void label_ops_reset(void) {
label_ops = NULL;
}
int label_ops_pre(int dir_fd, const char *path, mode_t mode) {
if (!label_ops || !label_ops->pre)
return 0;
@@ -24,9 +31,9 @@ int label_ops_pre(int dir_fd, const char *path, mode_t mode) {
return label_ops->pre(dir_fd, path, mode);
}
int label_ops_post(int dir_fd, const char *path) {
int label_ops_post(int dir_fd, const char *path, bool created) {
if (!label_ops || !label_ops->post)
return 0;
return label_ops->post(dir_fd, path);
return label_ops->post(dir_fd, path, created);
}

View File

@@ -1,14 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <sys/types.h>
typedef struct LabelOps {
int (*pre)(int dir_fd, const char *path, mode_t mode);
int (*post)(int dir_fd, const char *path);
int (*post)(int dir_fd, const char *path, bool created);
} LabelOps;
int label_ops_set(const LabelOps *label_ops);
void label_ops_reset(void);
int label_ops_pre(int dir_fd, const char *path, mode_t mode);
int label_ops_post(int dir_fd, const char *path);
int label_ops_post(int dir_fd, const char *path, bool created);

View File

@@ -279,8 +279,7 @@ int locale_is_installed(const char *name) {
if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */
return true;
_cleanup_(freelocalep) locale_t loc =
newlocale(LC_ALL_MASK, name, 0);
_cleanup_(freelocalep) locale_t loc = newlocale(LC_ALL_MASK, name, (locale_t) 0);
if (loc == (locale_t) 0)
return errno == ENOMEM ? -ENOMEM : false;

View File

@@ -2,6 +2,8 @@
#pragma once
#include <fcntl.h>
/* Include here so consumers have LOCK_{EX,SH,NB} available. */
#include <sys/file.h>
typedef struct LockFile {
int dir_fd;
@@ -17,7 +19,7 @@ static inline int make_lock_file(const char *p, int operation, LockFile *ret) {
int make_lock_file_for(const char *p, int operation, LockFile *ret);
void release_lock_file(LockFile *f);
#define LOCK_FILE_INIT { .dir_fd = -EBADF, .fd = -EBADF }
#define LOCK_FILE_INIT (LockFile) { .dir_fd = -EBADF, .fd = -EBADF }
/* POSIX locks with the same interface as flock(). */
int posix_lock(int fd, int operation);

View File

@@ -18,25 +18,26 @@ struct signalfd_siginfo;
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_SINGLE_MAX = LOG_TARGET_SYSLOG + 1,
_LOG_TARGET_MAX = LOG_TARGET_NULL + 1,
_LOG_TARGET_INVALID = -EINVAL,
} LogTarget;
/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
* used a regular log level. */
* used as a regular log level. */
#define LOG_NULL (LOG_EMERG - 1)
assert_cc(LOG_NULL == -1);
/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
#define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define SYNTHETIC_ERRNO(num) (abs(num) | (1 << 30))
#define IS_SYNTHETIC_ERRNO(val) (((val) >> 30) == 1)
#define ERRNO_VALUE(val) (abs(val) & ~(1 << 30))
/* The callback function to be invoked when syntax warnings are seen
@@ -48,7 +49,7 @@ static inline void clear_log_syntax_callback(dummy_t *dummy) {
set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
}
const char *log_target_to_string(LogTarget target) _const_;
const char* log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
void log_set_target(LogTarget target);
void log_set_target_and_open(LogTarget target);
@@ -56,7 +57,7 @@ int log_set_target_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
void log_settle_target(void);
void log_set_max_level(int level);
int log_set_max_level(int level);
int log_set_max_level_from_string(const char *e);
#if 0 /* NM_IGNORED */
int log_get_max_level(void) _pure_;
@@ -67,6 +68,7 @@ log_get_max_level(void)
return 7 /* LOG_DEBUG */;
}
#endif /* NM_IGNORED */
int log_max_levels_to_string(int level, char **ret);
void log_set_facility(int facility);
@@ -95,6 +97,7 @@ assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE __FILE__
#endif /* NM_IGNORED */
bool stderr_is_journal(void);
int log_open(void);
void log_close(void);
void log_forget_fds(void);
@@ -434,9 +437,10 @@ int log_emergency_level(void);
#define log_dump(level, buffer) \
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__)
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__)
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__)
#define log_oom_full(level) log_oom_internal(level, PROJECT_FILE, __LINE__, __func__)
#define log_oom() log_oom_full(LOG_ERR)
#define log_oom_debug() log_oom_full(LOG_DEBUG)
#define log_oom_warning() log_oom_full(LOG_WARNING)
bool log_on_console(void) _pure_;
@@ -498,6 +502,18 @@ int log_syntax_invalid_utf8_internal(
const char *func,
const char *rvalue);
int log_syntax_parse_error_internal(
const char *unit,
const char *config_file,
unsigned config_line,
int error,
bool critical, /* When true, propagate the passed error, otherwise this always returns 0. */
const char *file,
int line,
const char *func,
const char *lvalue,
const char *rvalue);
#define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \
int _level = (level), _e = (error); \
@@ -514,6 +530,12 @@ int log_syntax_invalid_utf8_internal(
: -EINVAL; \
})
#define log_syntax_parse_error_full(unit, config_file, config_line, error, critical, lvalue, rvalue) \
log_syntax_parse_error_internal(unit, config_file, config_line, error, critical, PROJECT_FILE, __LINE__, __func__, lvalue, rvalue)
#define log_syntax_parse_error(unit, config_file, config_line, error, lvalue, rvalue) \
log_syntax_parse_error_full(unit, config_file, config_line, error, /* critical = */ false, lvalue, rvalue)
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
void log_setup(void);
@@ -570,8 +592,8 @@ typedef struct LogRateLimit {
#define log_ratelimit_error_errno(error, ...) log_ratelimit_full_errno(LOG_ERR, error, __VA_ARGS__)
#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
const char *_log_set_prefix(const char *prefix, bool force);
static inline const char *_log_unset_prefixp(const char **p) {
const char* _log_set_prefix(const char *prefix, bool force);
static inline const char* _log_unset_prefixp(const char **p) {
assert(p);
_log_set_prefix(*p, true);
return NULL;
@@ -630,6 +652,15 @@ size_t log_context_num_contexts(void);
/* Returns the number of fields in all attached log contexts. */
size_t log_context_num_fields(void);
static inline void _reset_log_level(int *saved_log_level) {
assert(saved_log_level);
log_set_max_level(*saved_log_level);
}
#define LOG_CONTEXT_SET_LOG_LEVEL(level) \
_cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level);
#define LOG_CONTEXT_PUSH(...) \
LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))

View File

@@ -30,60 +30,6 @@
#define _function_no_sanitize_float_cast_overflow_
#endif
#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__)
/* Temporarily disable some warnings */
#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define DISABLE_WARNING_FORMAT_NONLITERAL \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
#define DISABLE_WARNING_MISSING_PROTOTYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
#define DISABLE_WARNING_NONNULL \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wnonnull\"")
#define DISABLE_WARNING_SHADOW \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
#if HAVE_WSTRINGOP_TRUNCATION
# define DISABLE_WARNING_STRINGOP_TRUNCATION \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
#else
# define DISABLE_WARNING_STRINGOP_TRUNCATION \
_Pragma("GCC diagnostic push")
#endif
#define DISABLE_WARNING_TYPE_LIMITS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
#define DISABLE_WARNING_ADDRESS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Waddress\"")
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
#else
#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT
#define DISABLE_WARNING_FORMAT_NONLITERAL
#define DISABLE_WARNING_MISSING_PROTOTYPES
#define DISABLE_WARNING_NONNULL
#define DISABLE_WARNING_SHADOW
#define REENABLE_WARNING
#endif
/* test harness */
#define EXIT_TEST_SKIP 77
@@ -256,16 +202,10 @@ static inline int __coverity_check_and_return__(int condition) {
#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
#define char_array_0(x) x[sizeof(x)-1] = 0;
#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member)
#define endoffsetof_field(struct_type, member) (offsetof(struct_type, member) + sizeof_field(struct_type, member))
/* Maximum buffer size needed for formatting an unsigned integer type as hex, including space for '0x'
* prefix and trailing NUL suffix. */
#define HEXADECIMAL_STR_MAX(type) (2 + sizeof(type) * 2 + 1)
@@ -311,15 +251,6 @@ static inline int __coverity_check_and_return__(int condition) {
/* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_MAX)
#define _FOREACH_ARRAY(i, array, num, m, end) \
for (typeof(array[0]) *i = (array), *end = ({ \
typeof(num) m = (num); \
(i && m > 0) ? i + m : NULL; \
}); end && i < end; i++)
#define FOREACH_ARRAY(i, array, num) \
_FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ))
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \

View File

@@ -20,7 +20,7 @@ size_t page_size(void) _pure_;
#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) {
static inline void* memcpy_safe(void *dst, const void *src, size_t n) {
if (n == 0)
return dst;
assert(src);
@@ -28,13 +28,20 @@ static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
}
/* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */
static inline void *mempcpy_safe(void *dst, const void *src, size_t n) {
static inline void* mempcpy_safe(void *dst, const void *src, size_t n) {
if (n == 0)
return dst;
assert(src);
return mempcpy(dst, src, n);
}
#define mempcpy_typesafe(dst, src, n) \
({ \
size_t _sz_; \
assert_se(MUL_SAFE(&_sz_, sizeof((dst)[0]), n)); \
(typeof((dst)[0])*) mempcpy_safe(dst, src, _sz_); \
})
/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */
static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
if (n == 0)

View File

@@ -7,6 +7,10 @@
#define F_LINUX_SPECIFIC_BASE 1024
#endif
#ifndef F_DUPFD_QUERY
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
#endif
#ifndef F_SETPIPE_SZ
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
#endif
@@ -92,3 +96,7 @@
#define RAW_O_LARGEFILE 00100000
#endif
#endif
#ifndef AT_HANDLE_FID
#define AT_HANDLE_FID AT_REMOVEDIR
#endif

View File

@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <linux/types.h>
#if HAVE_PIDFD_OPEN
#include <sys/pidfd.h>
#endif
#ifndef PIDFS_IOCTL_MAGIC
# define PIDFS_IOCTL_MAGIC 0xFF
#endif
#ifndef PIDFD_GET_CGROUP_NAMESPACE
# define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1)
# define PIDFD_GET_IPC_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 2)
# define PIDFD_GET_MNT_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 3)
# define PIDFD_GET_NET_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 4)
# define PIDFD_GET_PID_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 5)
# define PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 6)
# define PIDFD_GET_TIME_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 7)
# define PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 8)
# define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9)
# define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10)
#endif
#ifndef PIDFD_GET_INFO
struct pidfd_info {
__u64 mask;
__u64 cgroupid;
__u32 pid;
__u32 tgid;
__u32 ppid;
__u32 ruid;
__u32 rgid;
__u32 euid;
__u32 egid;
__u32 suid;
__u32 sgid;
__u32 fsuid;
__u32 fsgid;
__u32 spare0[1];
};
#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
#define PIDFD_INFO_PID (1UL << 0)
#define PIDFD_INFO_CREDS (1UL << 1)
#define PIDFD_INFO_CGROUPID (1UL << 2)
#endif

View File

@@ -1,20 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#if USE_SYS_RANDOM_H
#include "macro.h"
#if HAVE_GETRANDOM
# include <sys/random.h>
#else
# include <linux/random.h>
#endif
#ifndef GRND_NONBLOCK
#define GRND_NONBLOCK 0x0001
# define GRND_NONBLOCK 0x0001
#else
assert_cc(GRND_NONBLOCK == 0x0001);
#endif
#ifndef GRND_RANDOM
#define GRND_RANDOM 0x0002
# define GRND_RANDOM 0x0002
#else
assert_cc(GRND_RANDOM == 0x0002);
#endif
#ifndef GRND_INSECURE
#define GRND_INSECURE 0x0004
# define GRND_INSECURE 0x0004
#else
assert_cc(GRND_INSECURE == 0x0004);
#endif

View File

@@ -3,44 +3,6 @@
#include <sys/socket.h>
#if 0 /* NM_IGNORED */
#if HAVE_LINUX_VM_SOCKETS_H
#include <linux/vm_sockets.h>
#else
struct sockaddr_vm {
unsigned short svm_family;
unsigned short svm_reserved1;
unsigned int svm_port;
unsigned int svm_cid;
unsigned char svm_zero[sizeof(struct sockaddr) -
sizeof(unsigned short) -
sizeof(unsigned short) -
sizeof(unsigned int) -
sizeof(unsigned int)];
};
#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
@@ -53,6 +15,10 @@ struct sockaddr_vm {
#define SO_PEERGROUPS 59
#endif
#ifndef SO_PASSPIDFD
#define SO_PASSPIDFD 76
#endif
#ifndef SO_PEERPIDFD
#define SO_PEERPIDFD 77
#endif
@@ -74,11 +40,14 @@ struct sockaddr_vm {
#define SOL_SCTP 132
#endif
/* Not exposed yet. Defined in include/linux/socket.h */
#ifndef SCM_SECURITY
#define SCM_SECURITY 0x03
#endif
#ifndef SCM_PIDFD
#define SCM_PIDFD 0x04
#endif
/* netinet/in.h */
#ifndef IP_FREEBIND
#define IP_FREEBIND 15
@@ -100,7 +69,9 @@ struct sockaddr_vm {
#define IPV6_RECVFRAGSIZE 77
#endif
/* linux/sockios.h */
#ifndef SIOCGSKNS
#define SIOCGSKNS 0x894C
/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant, but very much
* useful for userspace too. It's documented in unix(7) these days, hence should be fairly reliable to define
* here. */
#ifndef SCM_MAX_FD
#define SCM_MAX_FD 253U
#endif

View File

@@ -22,6 +22,7 @@
#include "macro.h"
#include "missing_keyctl.h"
#include "missing_sched.h"
#include "missing_stat.h"
#include "missing_syscall_def.h"
@@ -81,12 +82,7 @@ static inline int missing_ioprio_set(int which, int who, int ioprio) {
#if !HAVE_MEMFD_CREATE
static inline int missing_memfd_create(const char *name, unsigned int flags) {
# ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define memfd_create missing_memfd_create
@@ -98,12 +94,7 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
#if !HAVE_GETRANDOM
/* glibc says getrandom() returns ssize_t */
static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
# ifdef __NR_getrandom
return syscall(__NR_getrandom, buffer, count, flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define getrandom missing_getrandom
@@ -150,12 +141,7 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil
#if !HAVE_SETNS
static inline int missing_setns(int fd, int nstype) {
# ifdef __NR_setns
return syscall(__NR_setns, fd, nstype);
# else
errno = ENOSYS;
return -1;
# endif
}
# define setns missing_setns
@@ -175,12 +161,7 @@ static inline pid_t raw_getpid(void) {
#if !HAVE_RENAMEAT2
static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
# ifdef __NR_renameat2
return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define renameat2 missing_renameat2
@@ -190,12 +171,7 @@ static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, c
#if !HAVE_KCMP
static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
# if defined __NR_kcmp && __NR_kcmp >= 0
return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
# else
errno = ENOSYS;
return -1;
# endif
}
# define kcmp missing_kcmp
@@ -205,34 +181,19 @@ static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long i
#if !HAVE_KEYCTL
static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
# if defined __NR_keyctl && __NR_keyctl >= 0
return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
# else
errno = ENOSYS;
return -1;
# endif
# define keyctl missing_keyctl
}
static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
# if defined __NR_add_key && __NR_add_key >= 0
return syscall(__NR_add_key, type, description, payload, plen, ringid);
# else
errno = ENOSYS;
return -1;
# endif
# define add_key missing_add_key
}
static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
# if defined __NR_request_key && __NR_request_key >= 0
return syscall(__NR_request_key, type, description, callout_info, destringid);
# else
errno = ENOSYS;
return -1;
# endif
# define request_key missing_request_key
}
@@ -344,12 +305,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
#if !HAVE_PIDFD_SEND_SIGNAL
static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
# ifdef __NR_pidfd_send_signal
return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define pidfd_send_signal missing_pidfd_send_signal
@@ -357,12 +313,7 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un
#if !HAVE_PIDFD_OPEN
static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
# ifdef __NR_pidfd_open
return syscall(__NR_pidfd_open, pid, flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define pidfd_open missing_pidfd_open
@@ -671,6 +622,17 @@ static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
# define getdents64 missing_getdents64
#endif
/* ======================================================================= */
#if !HAVE_SCHED_SETATTR
static inline ssize_t missing_sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) {
return syscall(__NR_sched_setattr, pid, attr, flags);
}
# define sched_setattr missing_sched_setattr
#endif
#endif /* NM_IGNORED */
/* ======================================================================= */
@@ -686,3 +648,21 @@ int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags,
* at build time) and just define it. Once the kernel drops ia64 support, we can drop this too. */
#define HAVE_CLONE 1
#endif
/* ======================================================================= */
#if 0 /* NM_IGNORED */
#if !HAVE_QUOTACTL_FD
static inline int missing_quotactl_fd(int fd, int cmd, int id, void *addr) {
#if defined __NR_quotactl_fd
return syscall(__NR_quotactl_fd, fd, cmd, id, addr);
#else
errno = ENOSYS;
return -1;
#endif
}
# define quotactl_fd missing_quotactl_fd
#endif
#endif /* NM_IGNORED */

View File

@@ -5,9 +5,7 @@
#if HAVE_THREADS_H
# include <threads.h>
#elif !(defined(thread_local))
/* Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
* see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 */
# if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
# ifndef __STDC_NO_THREADS__
# define thread_local _Thread_local
# else
# define thread_local __thread

View File

@@ -4,9 +4,9 @@
#include <uchar.h>
#if !HAVE_CHAR32_T
#define char32_t uint32_t
# define char32_t uint32_t
#endif
#if !HAVE_CHAR16_T
#define char16_t uint16_t
# define char16_t uint16_t
#endif

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/wait.h>
#include "macro.h"
#ifndef P_PIDFD
# define P_PIDFD 3
#else
assert_cc(P_PIDFD == 3);
#endif

View File

@@ -3,6 +3,8 @@
#include <sys/types.h>
#include "pidref.h"
typedef enum NamespaceType {
NAMESPACE_CGROUP,
NAMESPACE_IPC,
@@ -19,9 +21,23 @@ typedef enum NamespaceType {
extern const struct namespace_info {
const char *proc_name;
const char *proc_path;
unsigned int clone_flag;
unsigned long clone_flag;
unsigned long pidfd_get_ns_ioctl_cmd;
ino_t root_inode;
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag);
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
int namespace_open_by_type(NamespaceType type);
int pidref_namespace_open(
const PidRef *pidref,
int *ret_pidns_fd,
int *ret_mntns_fd,
int *ret_netns_fd,
int *ret_userns_fd,
int *ret_root_fd);
int namespace_open(
pid_t pid,
int *ret_pidns_fd,
@@ -29,11 +45,28 @@ int namespace_open(
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 fd_is_namespace(int fd, NamespaceType type);
int is_our_namespace(int fd, NamespaceType type);
int namespace_is_init(NamespaceType type);
int pidref_in_same_namespace(PidRef *pid1, PidRef *pid2, NamespaceType type);
static inline int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
assert(pid1 >= 0);
assert(pid2 >= 0);
return pidref_in_same_namespace(pid1 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid1),
pid2 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid2),
type);
}
int namespace_get_leader(PidRef *pidref, NamespaceType type, PidRef *ret);
int detach_mount_namespace(void);
int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
int detach_mount_namespace_userns(int userns_fd);
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
@@ -50,8 +83,16 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
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);
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
int userns_acquire_empty(void);
int userns_acquire(const char *uid_map, const char *gid_map);
int userns_enter_and_pin(int userns_fd, pid_t *ret_pid);
int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
int process_is_owned_by_uid(const PidRef *pidref, uid_t uid);
int is_idmapping_supported(const char *path);
int netns_acquire(void);

View File

@@ -4,6 +4,7 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/ipv6.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
@@ -640,7 +641,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
s = *p;
/* accept any number of digits, strtoull is limited to 19 */
for (size_t i = 0; i < digits; i++,s++) {
for (size_t i = 0; i < digits; i++, s++) {
if (!ascii_isdigit(*s)) {
if (i == 0)
return -EINVAL;
@@ -721,22 +722,6 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow
return 0;
}
int parse_ip_prefix_length(const char *s, int *ret) {
unsigned l;
int r;
r = safe_atou(s, &l);
if (r < 0)
return r;
if (l > 128)
return -ERANGE;
*ret = (int) l;
return 0;
}
int parse_oom_score_adjust(const char *s, int *ret) {
int r, v;

View File

@@ -141,8 +141,6 @@ 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, bool allow_zero);
int parse_ip_prefix_length(const char *s, int *ret);
int parse_oom_score_adjust(const char *s, int *ret);
/* Implement floating point using fixed integers, to improve performance when

View File

@@ -54,6 +54,7 @@ char* path_make_absolute(const char *p, const char *prefix) {
return path_join(prefix, p);
}
#endif /* NM_IGNORED */
int safe_getcwd(char **ret) {
_cleanup_free_ char *cwd = NULL;
@@ -73,6 +74,7 @@ int safe_getcwd(char **ret) {
return 0;
}
#if 0 /* NM_IGNORED */
int path_make_absolute_cwd(const char *p, char **ret) {
char *c;
int r;
@@ -221,8 +223,10 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret
}
#endif /* NM_IGNORED */
char* path_startswith_strv(const char *p, char **set) {
STRV_FOREACH(s, set) {
char* path_startswith_strv(const char *p, char * const *strv) {
assert(p);
STRV_FOREACH(s, strv) {
char *t;
t = path_startswith(p, *s);
@@ -531,6 +535,20 @@ int path_compare_filename(const char *a, const char *b) {
return strcmp(fa, fb);
}
#if 0 /* NM_IGNORED */
int path_equal_or_inode_same_full(const char *a, const char *b, int flags) {
/* Returns true if paths are of the same entry, false if not, <0 on error. */
if (path_equal(a, b))
return 1;
if (!a || !b)
return 0;
return inode_same(a, b, flags);
}
#endif /* NM_IGNORED */
char* path_extend_internal(char **x, ...) {
size_t sz, old_sz;
char *q, *nx;
@@ -662,6 +680,8 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
r = path_make_absolute_cwd(name, ret_filename);
if (r < 0)
return r;
path_simplify(*ret_filename);
}
if (ret_fd)
@@ -673,48 +693,49 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
int find_executable_full(
const char *name,
const char *root,
char **exec_search_path,
char * const *exec_search_path,
bool use_path_envvar,
char **ret_filename,
int *ret_fd) {
int last_error = -ENOENT, r = 0;
const char *p = NULL;
assert(name);
if (is_path(name))
return find_executable_impl(name, root, ret_filename, ret_fd);
if (use_path_envvar)
/* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
* binary. */
p = getenv("PATH");
if (!p)
p = DEFAULT_PATH;
if (exec_search_path) {
STRV_FOREACH(element, exec_search_path) {
_cleanup_free_ char *full_path = NULL;
if (!path_is_absolute(*element))
if (!path_is_absolute(*element)) {
log_debug("Exec search path '%s' isn't absolute, ignoring.", *element);
continue;
}
full_path = path_join(*element, name);
if (!full_path)
return -ENOMEM;
r = find_executable_impl(full_path, root, ret_filename, ret_fd);
if (r < 0) {
if (r >= 0)
return 0;
if (r != -EACCES)
last_error = r;
continue;
}
return 0;
}
return last_error;
}
const char *p = NULL;
if (use_path_envvar)
/* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
* binary. */
p = getenv("PATH");
if (!p)
p = default_PATH();
/* Resolve a single-component name to a full path */
for (;;) {
_cleanup_free_ char *element = NULL;
@@ -725,22 +746,20 @@ int find_executable_full(
if (r == 0)
break;
if (!path_is_absolute(element))
if (!path_is_absolute(element)) {
log_debug("Exec search path '%s' isn't absolute, ignoring.", element);
continue;
}
if (!path_extend(&element, name))
return -ENOMEM;
r = find_executable_impl(element, root, ret_filename, ret_fd);
if (r < 0) {
if (r >= 0) /* Found it! */
return 0;
/* PATH entries which we don't have access to are ignored, as per tradition. */
if (r != -EACCES)
last_error = r;
continue;
}
/* Found it! */
return 0;
}
return last_error;
@@ -1102,7 +1121,6 @@ int path_extract_filename(const char *path, char **ret) {
}
int path_extract_directory(const char *path, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c, *next = NULL;
int r;
@@ -1126,14 +1144,10 @@ int path_extract_directory(const char *path, char **ret) {
if (*path != '/') /* filename only */
return -EDESTADDRREQ;
a = strdup("/");
if (!a)
return -ENOMEM;
*ret = TAKE_PTR(a);
return 0;
return strdup_to(ret, "/");
}
a = strndup(path, next - path);
_cleanup_free_ char *a = strndup(path, next - path);
if (!a)
return -ENOMEM;
@@ -1347,6 +1361,20 @@ bool dot_or_dot_dot(const char *path) {
}
#if 0 /* NM_IGNORED */
bool path_implies_directory(const char *path) {
/* Sometimes, if we look at a path we already know it must refer to a directory, because it is
* suffixed with a slash, or its last component is "." or ".." */
if (!path)
return false;
if (dot_or_dot_dot(path))
return true;
return ENDSWITH_SET(path, "/", "/.", "/..");
}
bool empty_or_root(const char *path) {
/* For operations relative to some root directory, returns true if the specified root directory is
@@ -1358,7 +1386,9 @@ bool empty_or_root(const char *path) {
return path_equal(path, "/");
}
bool path_strv_contains(char **l, const char *path) {
bool path_strv_contains(char * const *l, const char *path) {
assert(path);
STRV_FOREACH(i, l)
if (path_equal(*i, path))
return true;
@@ -1366,7 +1396,9 @@ bool path_strv_contains(char **l, const char *path) {
return false;
}
bool prefixed_path_strv_contains(char **l, const char *path) {
bool prefixed_path_strv_contains(char * const *l, const char *path) {
assert(path);
STRV_FOREACH(i, l) {
const char *j = *i;
@@ -1374,6 +1406,7 @@ bool prefixed_path_strv_contains(char **l, const char *path) {
j++;
if (*j == '+')
j++;
if (path_equal(j, path))
return true;
}
@@ -1443,4 +1476,32 @@ int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
*ret = NULL;
return false;
}
const char* default_PATH(void) {
#if HAVE_SPLIT_BIN
static int split = -1;
int r;
/* Check whether /usr/sbin is not a symlink and return the appropriate $PATH.
* On error fall back to the safe value with both directories as configured… */
if (split < 0)
STRV_FOREACH_PAIR(bin, sbin, STRV_MAKE("/usr/bin", "/usr/sbin",
"/usr/local/bin", "/usr/local/sbin")) {
r = inode_same(*bin, *sbin, AT_NO_AUTOMOUNT);
if (r > 0 || r == -ENOENT)
continue;
if (r < 0)
log_debug_errno(r, "Failed to compare \"%s\" and \"%s\", using compat $PATH: %m",
*bin, *sbin);
split = true;
break;
}
if (split < 0)
split = false;
if (split)
return DEFAULT_PATH_WITH_SBIN;
#endif
return DEFAULT_PATH_WITHOUT_SBIN;
}
#endif /* NM_IGNORED */

View File

@@ -12,27 +12,26 @@
#include "time-util.h"
#if 0 /* NM_IGNORED */
#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin"
#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0"
#define PATH_SPLIT_BIN(x) x "sbin:" x "bin"
#define PATH_SPLIT_BIN_NULSTR(x) x "sbin\0" x "bin\0"
#define PATH_NORMAL_SBIN_BIN(x) x "bin"
#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0"
#define PATH_MERGED_BIN(x) x "bin"
#define PATH_MERGED_BIN_NULSTR(x) x "bin\0"
#if HAVE_SPLIT_BIN
# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x)
# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x)
#define DEFAULT_PATH_WITH_SBIN PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/")
#define DEFAULT_PATH_WITHOUT_SBIN PATH_MERGED_BIN("/usr/local/") ":" PATH_MERGED_BIN("/usr/")
#define DEFAULT_PATH_COMPAT PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/") ":" PATH_SPLIT_BIN("/")
const char* default_PATH(void);
static inline const char* default_user_PATH(void) {
#ifdef DEFAULT_USER_PATH
return DEFAULT_USER_PATH;
#else
# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x)
# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
#endif
#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("/")
#ifndef DEFAULT_USER_PATH
# define DEFAULT_USER_PATH DEFAULT_PATH
return default_PATH();
#endif
}
#endif /* NM_IGNORED */
static inline bool is_path(const char *p) {
@@ -70,14 +69,19 @@ static inline bool path_equal_filename(const char *a, const char *b) {
return path_compare_filename(a, b) == 0;
}
int path_equal_or_inode_same_full(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;
return path_equal_or_inode_same_full(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)
static inline char* skip_leading_slash(const char *p) {
return skip_leading_chars(p, "/");
}
typedef enum PathSimplifyFlags {
PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
} PathSimplifyFlags;
@@ -103,21 +107,23 @@ static inline int path_simplify_alloc(const char *path, char **ret) {
return 0;
}
static inline bool path_equal_ptr(const char *a, const char *b) {
return !!a == !!b && (!a || path_equal(a, b));
}
/* Note: the search terminates on the first NULL item. */
#define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p)
char* path_startswith_strv(const char *p, char **set);
char* path_startswith_strv(const char *p, char * const *strv);
#define PATH_STARTSWITH_SET(p, ...) path_startswith_strv(p, STRV_MAKE(__VA_ARGS__))
int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(char **l, const char *root);
int find_executable_full(const char *name, 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 * const *exec_search_path,
bool use_path_envvar,
char **ret_filename,
int *ret_fd);
static inline int find_executable(const char *name, char **ret_filename) {
return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL);
}
@@ -203,7 +209,9 @@ bool valid_device_allow_pattern(const char *path);
bool dot_or_dot_dot(const char *path);
static inline const char *skip_dev_prefix(const char *p) {
bool path_implies_directory(const char *path);
static inline const char* skip_dev_prefix(const char *p) {
const char *e;
/* Drop any /dev prefix if there is any */
@@ -218,7 +226,7 @@ static inline const char* empty_to_root(const char *path) {
return isempty(path) ? "/" : path;
}
bool path_strv_contains(char **l, const char *path);
bool prefixed_path_strv_contains(char **l, const char *path);
bool path_strv_contains(char * const *l, const char *path);
bool prefixed_path_strv_contains(char * const *l, const char *path);
int path_glob_can_match(const char *pattern, const char *prefix, char **ret);

View File

@@ -0,0 +1,279 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-shared.h"
#include <sys/ioctl.h>
#include <unistd.h>
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "macro.h"
#include "memory-util.h"
#include "missing_magic.h"
#include "missing_threads.h"
#include "parse-util.h"
#include "path-util.h"
#include "pidfd-util.h"
#include "process-util.h"
#include "stat-util.h"
#include "string-util.h"
static int have_pidfs = -1;
static int pidfd_check_pidfs(int pid_fd) {
/* NB: the passed fd *must* be acquired via pidfd_open(), i.e. must be a true pidfd! */
if (have_pidfs >= 0)
return have_pidfs;
return (have_pidfs = fd_is_fs_type(pid_fd, PID_FS_MAGIC));
}
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
static bool cached_supported = true;
/* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
*
* Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
* is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
* see below) */
assert(fd >= 0);
/* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
* call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
* anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
if (have_pidfs == 0 || !cached_supported)
return -EOPNOTSUPP;
int nsfd = ioctl(fd, ns_type_cmd);
if (nsfd < 0) {
/* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
* at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
if (IN_SET(errno, ENOTTY, EINVAL)) {
cached_supported = false;
return -EOPNOTSUPP;
}
if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
return -ENOPKG;
return -errno;
}
return nsfd;
}
#if 0 /* NM_IGNORED */
static int pidfd_get_info(int fd, struct pidfd_info *info) {
static bool cached_supported = true;
assert(fd >= 0);
assert(info);
if (have_pidfs == 0 || !cached_supported)
return -EOPNOTSUPP;
if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
cached_supported = false;
return -EOPNOTSUPP;
}
return -errno;
}
return 0;
}
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;
int r;
assert(fd >= 0);
xsprintf(path, "/proc/self/fdinfo/%i", fd);
r = read_full_virtual_file(path, &fdinfo, NULL);
if (r == -ENOENT)
return proc_fd_enoent_errno();
if (r < 0)
return r;
char *p = find_line_startswith(fdinfo, "Pid:");
if (!p)
return -ENOTTY; /* not a pidfd? */
p = skip_leading_chars(p, /* bad = */ NULL);
p[strcspn(p, WHITESPACE)] = 0;
if (streq(p, "0"))
return -EREMOTE; /* PID is in foreign PID namespace? */
if (streq(p, "-1"))
return -ESRCH; /* refers to reaped process? */
return parse_pid(p, ret);
}
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
if (ret)
*ret = info.pid;
return 0;
}
int pidfd_get_pid(int fd, pid_t *ret) {
int r;
/* Converts a pidfd into a pid. We try ioctl(PIDFD_GET_INFO) (kernel 6.13+) first,
* /proc/self/fdinfo/ as fallback. Well known errors:
*
* -EBADF → fd invalid
* -ESRCH → fd valid, but process is already reaped
*
* pidfd_get_pid_fdinfo() might additionally fail for other reasons:
*
* -ENOSYS → /proc/ not mounted
* -ENOTTY → fd valid, but not a pidfd
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
* (when using PIDFD_GET_INFO this is indistinguishable from -ESRCH)
*/
assert(fd >= 0);
r = pidfd_get_pid_ioctl(fd, ret);
if (r != -EOPNOTSUPP)
return r;
return pidfd_get_pid_fdinfo(fd, ret);
}
int pidfd_verify_pid(int pidfd, pid_t pid) {
pid_t current_pid;
int r;
assert(pidfd >= 0);
assert(pid > 0);
r = pidfd_get_pid(pidfd, &current_pid);
if (r < 0)
return r;
return current_pid != pid ? -ESRCH : 0;
}
int pidfd_get_ppid(int fd, pid_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
if (info.ppid == 0) /* See comments in pid_get_ppid() */
return -EADDRNOTAVAIL;
if (ret)
*ret = info.ppid;
return 0;
}
int pidfd_get_uid(int fd, uid_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
if (ret)
*ret = info.ruid;
return 0;
}
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
if (ret)
*ret = info.cgroupid;
return 0;
}
#endif /* NM_IGNORED */
int pidfd_get_inode_id(int fd, uint64_t *ret) {
int r;
assert(fd >= 0);
r = pidfd_check_pidfs(fd);
if (r < 0)
return r;
if (r == 0)
return -EOPNOTSUPP;
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
if (ret)
*ret = (uint64_t) st.st_ino;
return 0;
}
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
static thread_local uint64_t cached = 0;
static thread_local pid_t initialized = 0; /* < 0: cached error; == 0: invalid; > 0: valid and pid that was current */
int r;
assert(ret);
if (initialized == getpid_cached()) {
*ret = cached;
return 0;
}
if (initialized < 0)
return initialized;
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
if (fd < 0)
return -errno;
r = pidfd_get_inode_id(fd, &cached);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return (initialized = -EOPNOTSUPP);
if (r < 0)
return r;
*ret = cached;
initialized = getpid_cached();
return 0;
}

View File

@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdint.h>
#include <sys/types.h>
#include "missing_pidfd.h"
#include "missing_syscall.h"
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd);
int pidfd_get_pid(int fd, pid_t *ret);
int pidfd_verify_pid(int pidfd, pid_t pid);
int pidfd_get_ppid(int fd, pid_t *ret);
int pidfd_get_uid(int fd, uid_t *ret);
int pidfd_get_cgroupid(int fd, uint64_t *ret);
int pidfd_get_inode_id(int fd, uint64_t *ret);
int pidfd_get_inode_id_self_cached(uint64_t *ret);

View File

@@ -1,15 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
typedef struct PidRef PidRef;
#include "macro.h"
#include "process-util.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;
/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes
* continuously. This combines a PID, a modern Linux pidfd and the 64bit inode number of the pidfd into one
* structure. Note that depending on kernel support the pidfd might not be initialized, and if it is
* initialized then fd_id might still not be initialized (because the concept was added to the kernel much
* later than pidfds themselves).
*
* There are three special states a PidRef can be in:
*
* 1. It can be *unset*. Use pidref_is_set() to detect this case. Most operations attempted on such a PidRef
* will fail with -ESRCH. Use PIDREF_NULL for initializing a PidRef in this state.
*
* 2. It can be marked as *automatic*. This is a special state indicating that a process reference is
* supposed to be derived automatically from the current context. This is used by the Varlink/JSON
* dispatcher as indication that a PidRef shall be derived from the connection peer, but might be
* otherwise used too. When marked *automatic* the PidRef will also be considered *unset*, hence most
* operations will fail with -ESRCH, as above.
*
* 3. It can be marked as *remote*. This is useful when deserializing a PidRef structure from an IPC message
* or similar, and it has been determined that the given PID definitely doesn't refer to a local
* process. In this case the PidRef logic will refrain from trying to acquire a pidfd for the
* process. Moreover, most operations will fail with -EREMOTE. Only PidRef structures that are not marked
* *unset* can be marked *remote*.
*/
struct PidRef {
pid_t pid; /* > 0 if the PidRef is set, otherwise set to PID_AUTOMATIC if automatic mode is
* desired, or 0 otherwise. */
int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd. If we
* know that the PID is not from the local machine we set this to -EREMOTE, otherwise
* we use -EBADF as indicator the fd is invalid. */
uint64_t fd_id; /* the inode number of pidfd. only useful in kernel 6.9+ where pidfds live in
their own pidfs and each process comes with a unique inode number */
};
#define PIDREF_NULL (const PidRef) { .fd = -EBADF }
#define PIDREF_NULL (PidRef) { .fd = -EBADF }
/* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding
* context, for example connection peer. Much like PIDREF_NULL it will be considered unset by
* pidref_is_set().*/
#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .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) */
@@ -19,18 +54,17 @@ 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) {
bool pidref_is_automatic(const PidRef *pidref);
if (pidref_is_set(a)) {
if (!pidref_is_set(b))
return false;
return a->pid == b->pid;
}
return !pidref_is_set(b);
static inline bool pidref_is_remote(const PidRef *pidref) {
/* If the fd is set to -EREMOTE we assume PidRef does not refer to a local PID, but on another
* machine (and we just got the PidRef initialized due to deserialization of some RPC message) */
return pidref_is_set(pidref) && pidref->fd == -EREMOTE;
}
int pidref_acquire_pidfd_id(PidRef *pidref);
bool pidref_equal(PidRef *a, PidRef *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);
@@ -43,13 +77,13 @@ static inline int pidref_set_self(PidRef *pidref) {
return pidref_set_pid(pidref, 0);
}
bool pidref_is_self(const PidRef *pidref);
bool pidref_is_self(PidRef *pidref);
void pidref_done(PidRef *pidref);
PidRef *pidref_free(PidRef *pidref);
PidRef* pidref_free(PidRef *pidref);
DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free);
int pidref_copy(const PidRef *pidref, PidRef *dest);
int pidref_copy(const PidRef *pidref, PidRef *ret);
int pidref_dup(const PidRef *pidref, PidRef **ret);
int pidref_new_from_pid(pid_t pid, PidRef **ret);
@@ -58,8 +92,8 @@ 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);
int pidref_wait(PidRef *pidref, siginfo_t *siginfo, int options);
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret);
static inline void pidref_done_sigkill_wait(PidRef *pidref) {
if (!pidref_is_set(pidref))

View File

@@ -26,8 +26,7 @@ struct prioq_item {
struct Prioq {
compare_func_t compare_func;
unsigned n_items, n_allocated;
unsigned n_items;
struct prioq_item *items;
};
@@ -144,28 +143,18 @@ static unsigned shuffle_down(Prioq *q, unsigned idx) {
}
int prioq_put(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
if (q->n_items >= q->n_allocated) {
unsigned n;
struct prioq_item *j;
n = MAX((q->n_items+1) * 2, 16u);
j = reallocarray(q->items, n, sizeof(struct prioq_item));
if (!j)
if (!GREEDY_REALLOC(q->items, MAX(q->n_items + 1, 16u)))
return -ENOMEM;
q->items = j;
q->n_allocated = n;
}
k = q->n_items++;
i = q->items + k;
i->data = data;
i->idx = idx;
q->items[k] = (struct prioq_item) {
.data = data,
.idx = idx,
};
if (idx)
*idx = k;

View File

@@ -27,6 +27,7 @@
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
#include "cgroup-util.h"
#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
@@ -36,6 +37,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "io-util.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
@@ -48,6 +50,7 @@
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "pidfd-util.h"
#include "process-util.h"
#include "raw-clone.h"
#include "rlimit-util.h"
@@ -57,6 +60,7 @@
#include "string-table.h"
#include "string-util.h"
#include "terminal-util.h"
#include "time-util.h"
#include "user-util.h"
#include "utf8.h"
@@ -103,8 +107,8 @@ int pid_get_comm(pid_t pid, char **ret) {
_cleanup_free_ char *escaped = NULL, *comm = NULL;
int r;
assert(ret);
assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached()) {
comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */
@@ -144,6 +148,9 @@ int pidref_get_comm(const PidRef *pid, char **ret) {
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_comm(pid->pid, &comm);
if (r < 0)
return r;
@@ -290,6 +297,9 @@ int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlag
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
if (r < 0)
return r;
@@ -332,6 +342,9 @@ int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char *
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_cmdline_strv(pid->pid, flags, &args);
if (r < 0)
return r;
@@ -387,33 +400,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0;
}
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;
@@ -478,6 +464,9 @@ int pidref_is_kernel_thread(const PidRef *pid) {
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
result = pid_is_kernel_thread(pid->pid);
if (result < 0)
return result;
@@ -489,22 +478,6 @@ int pidref_is_kernel_thread(const PidRef *pid) {
return result;
}
int get_process_capeff(pid_t pid, char **ret) {
const char *p;
int r;
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "status");
r = get_proc_field(p, "CapEff", WHITESPACE, ret);
if (r == -ENOENT)
return -ESRCH;
return r;
}
static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
const char *p;
int r;
@@ -589,12 +562,21 @@ int pid_get_uid(pid_t pid, uid_t *ret) {
}
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
uid_t uid;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
if (pid->fd >= 0) {
r = pidfd_get_uid(pid->fd, ret);
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
}
uid_t uid;
r = pid_get_uid(pid->pid, &uid);
if (r < 0)
return r;
@@ -680,7 +662,7 @@ int get_process_environ(pid_t pid, char **ret) {
return 0;
}
int get_process_ppid(pid_t pid, pid_t *ret) {
int pid_get_ppid(pid_t pid, pid_t *ret) {
_cleanup_free_ char *line = NULL;
unsigned long ppid;
const char *p;
@@ -688,15 +670,17 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
assert(pid >= 0);
if (pid == 0 || pid == getpid_cached()) {
if (pid == 0)
pid = getpid_cached();
if (pid == 1) /* PID 1 has no parent, shortcut this case */
return -EADDRNOTAVAIL;
if (pid == getpid_cached()) {
if (ret)
*ret = getppid();
return 0;
}
if (pid == 1) /* PID 1 has no parent, shortcut this case */
return -EADDRNOTAVAIL;
p = procfs_file_alloca(pid, "stat");
r = read_one_line_file(p, &line);
if (r == -ENOENT)
@@ -710,7 +694,6 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
p = strrchr(line, ')');
if (!p)
return -EIO;
p++;
if (sscanf(p, " "
@@ -719,9 +702,9 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
&ppid) != 1)
return -EIO;
/* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for
* processes originating in other namespaces that are inserted into a pidns. Return a recognizable
* error in this case. */
/* If ppid is zero the process has no parent. Which might be the case for PID 1 (caught above)
* but also for processes originating in other namespaces that are inserted into a pidns.
* Return a recognizable error in this case. */
if (ppid == 0)
return -EADDRNOTAVAIL;
@@ -734,7 +717,74 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0;
}
int pid_get_start_time(pid_t pid, uint64_t *ret) {
int pidref_get_ppid(const PidRef *pidref, pid_t *ret) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
if (pidref_is_remote(pidref))
return -EREMOTE;
if (pidref->fd >= 0) {
r = pidfd_get_ppid(pidref->fd, ret);
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
}
pid_t ppid;
r = pid_get_ppid(pidref->pid, ret ? &ppid : NULL);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret)
*ret = ppid;
return 0;
}
int pidref_get_ppid_as_pidref(const PidRef *pidref, PidRef *ret) {
pid_t ppid;
int r;
assert(ret);
r = pidref_get_ppid(pidref, &ppid);
if (r < 0)
return r;
for (unsigned attempt = 0; attempt < 16; attempt++) {
_cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
r = pidref_set_pid(&parent, ppid);
if (r < 0)
return r;
/* If we have a pidfd of the original PID, let's verify that the process we acquired really
* is the parent still */
if (pidref->fd >= 0) {
r = pidref_get_ppid(pidref, &ppid);
if (r < 0)
return r;
/* Did the PPID change since we queried it? if so we might have pinned the wrong
* process, if its PID got reused by now. Let's try again */
if (parent.pid != ppid)
continue;
}
*ret = TAKE_PIDREF(parent);
return 0;
}
/* Give up after 16 tries */
return -ENOTRECOVERABLE;
}
int pid_get_start_time(pid_t pid, usec_t *ret) {
_cleanup_free_ char *line = NULL;
const char *p;
int r;
@@ -754,7 +804,6 @@ int pid_get_start_time(pid_t pid, uint64_t *ret) {
p = strrchr(line, ')');
if (!p)
return -EIO;
p++;
unsigned long llu;
@@ -784,18 +833,21 @@ int pid_get_start_time(pid_t pid, uint64_t *ret) {
return -EIO;
if (ret)
*ret = llu;
*ret = jiffies_to_usec(llu); /* CLOCK_BOOTTIME */
return 0;
}
int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
uint64_t t;
int pidref_get_start_time(const PidRef *pid, usec_t *ret) {
usec_t t;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_start_time(pid->pid, ret ? &t : NULL);
if (r < 0)
return r;
@@ -1026,7 +1078,6 @@ int kill_and_sigcont(pid_t pid, int sig) {
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 sum = 0;
int r;
@@ -1035,22 +1086,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
assert(field);
assert(ret);
if (pid == 0 || pid == getpid_cached()) {
const char *e;
e = getenv(field);
if (!e) {
*ret = NULL;
return 0;
}
value = strdup(e);
if (!value)
return -ENOMEM;
*ret = value;
return 1;
}
if (pid == 0 || pid == getpid_cached())
return strdup_to_full(ret, getenv(field));
if (!pid_is_valid(pid))
return -EINVAL;
@@ -1079,78 +1116,55 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
sum += r;
match = startswith(line, field);
if (match && *match == '=') {
value = strdup(match + 1);
if (!value)
return -ENOMEM;
*ret = value;
return 1;
}
if (match && *match == '=')
return strdup_to_full(ret, match + 1);
}
*ret = NULL;
return 0;
}
int pid_is_my_child(pid_t pid) {
pid_t ppid;
int pidref_is_my_child(PidRef *pid) {
int r;
if (pid < 0)
if (!pidref_is_set(pid))
return -ESRCH;
if (pid <= 1)
if (pidref_is_remote(pid))
return -EREMOTE;
if (pid->pid == 1 || pidref_is_self(pid))
return false;
r = get_process_ppid(pid, &ppid);
pid_t ppid;
r = pidref_get_ppid(pid, &ppid);
if (r == -EADDRNOTAVAIL) /* if this process is outside of our pidns, it is definitely not our child */
return false;
if (r < 0)
return r;
return ppid == getpid_cached();
}
int pidref_is_my_child(const PidRef *pid) {
int r, result;
int pid_is_my_child(pid_t pid) {
if (!pidref_is_set(pid))
return -ESRCH;
if (pid == 0)
return false;
result = pid_is_my_child(pid->pid);
if (result < 0)
return result;
r = pidref_verify(pid);
if (r < 0)
return r;
return result;
return pidref_is_my_child(&PIDREF_MAKE_FROM_PID(pid));
}
int pid_is_unwaited(pid_t pid) {
/* Checks whether a PID is still valid at all, including a zombie */
if (pid < 0)
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;
if (pid == getpid_cached())
return true;
if (kill(pid, 0) >= 0)
return true;
return errno != ESRCH;
}
int pidref_is_unwaited(const PidRef *pid) {
int pidref_is_unwaited(PidRef *pid) {
int r;
/* Checks whether a PID is still valid at all, including a zombie */
if (!pidref_is_set(pid))
return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
if (pid->pid == 1 || pidref_is_self(pid))
return true;
@@ -1163,6 +1177,14 @@ int pidref_is_unwaited(const PidRef *pid) {
return true;
}
int pid_is_unwaited(pid_t pid) {
if (pid == 0)
return true;
return pidref_is_unwaited(&PIDREF_MAKE_FROM_PID(pid));
}
int pid_is_alive(pid_t pid) {
int r;
@@ -1192,6 +1214,9 @@ int pidref_is_alive(const PidRef *pidref) {
if (!pidref_is_set(pidref))
return -ESRCH;
if (pidref_is_remote(pidref))
return -EREMOTE;
result = pid_is_alive(pidref->pid);
if (result < 0) {
assert(result != -ESRCH);
@@ -1207,28 +1232,63 @@ int pidref_is_alive(const PidRef *pidref) {
return result;
}
int pid_from_same_root_fs(pid_t pid) {
const char *root;
int pidref_from_same_root_fs(PidRef *a, PidRef *b) {
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
int r;
if (pid < 0)
/* Checks if the two specified processes have the same root fs. Either can be specified as NULL in
* which case we'll check against ourselves. */
if (!a || !b) {
r = pidref_set_self(&self);
if (r < 0)
return r;
if (!a)
a = &self;
if (!b)
b = &self;
}
if (!pidref_is_set(a) || !pidref_is_set(b))
return -ESRCH;
/* If one of the two processes have the same root they cannot have the same root fs, but if both of
* them do we don't know */
if (pidref_is_remote(a) && pidref_is_remote(b))
return -EREMOTE;
if (pidref_is_remote(a) || pidref_is_remote(b))
return false;
if (pid == 0 || pid == getpid_cached())
if (pidref_equal(a, b))
return true;
root = procfs_file_alloca(pid, "root");
const char *roota = procfs_file_alloca(a->pid, "root");
const char *rootb = procfs_file_alloca(b->pid, "root");
return inode_same(root, "/proc/1/root", 0);
int result = inode_same(roota, rootb, 0);
if (result == -ENOENT)
return proc_mounted() == 0 ? -ENOSYS : -ESRCH;
if (result < 0)
return result;
r = pidref_verify(a);
if (r < 0)
return r;
r = pidref_verify(b);
if (r < 0)
return r;
return result;
}
#endif /* NM_IGNORED */
bool is_main_thread(void) {
static thread_local int cached = 0;
static thread_local int cached = -1;
if (_unlikely_(cached == 0))
cached = getpid_cached() == gettid() ? 1 : -1;
if (cached < 0)
cached = getpid_cached() == gettid();
return cached > 0;
return cached;
}
#if 0 /* NM_IGNORED */
@@ -1423,11 +1483,6 @@ int must_be_root(void) {
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
}
static void restore_sigsetp(sigset_t **ssp) {
if (*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;
@@ -1467,6 +1522,11 @@ pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
return pid;
}
static void restore_sigsetp(sigset_t **ssp) {
if (*ssp)
(void) sigprocmask(SIG_SETMASK, *ssp, NULL);
}
static int fork_flags_to_signal(ForkFlags flags) {
return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
(flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
@@ -1487,8 +1547,8 @@ int safe_fork_full(
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));
assert(!FLAGS_SET(flags, FORK_DETACH) ||
(!ret_pid && (flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0));
/* 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. */
@@ -1519,15 +1579,12 @@ int safe_fork_full(
}
if (block_signals) {
if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
return log_full_errno(prio, errno, "Failed to set signal mask: %m");
if (sigprocmask(SIG_BLOCK, &ss, &saved_ss) < 0)
return log_full_errno(prio, errno, "Failed to block signal mask: %m");
saved_ssp = &saved_ss;
}
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();
@@ -1549,11 +1606,12 @@ int safe_fork_full(
}
}
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS|FORK_NEW_PIDNS)) != 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_NETNS) ? CLONE_NEWNET : 0));
(FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0) |
(FLAGS_SET(flags, FORK_NEW_PIDNS) ? CLONE_NEWPID : 0));
else
pid = fork();
if (pid < 0)
@@ -1748,6 +1806,9 @@ int safe_fork_full(
}
}
if (FLAGS_SET(flags, FORK_FREEZE))
freeze();
if (ret_pid)
*ret_pid = getpid_cached();
@@ -1765,12 +1826,16 @@ int pidref_safe_fork_full(
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)
if (r < 0 || !ret_pid)
return r;
if (r > 0 && FLAGS_SET(flags, FORK_WAIT)) {
/* If we are in the parent and successfully waited, then the process doesn't exist anymore */
*ret_pid = PIDREF_NULL;
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);
@@ -1841,6 +1906,9 @@ int namespace_fork(
int set_oom_score_adjust(int value) {
char t[DECIMAL_STR_MAX(int)];
if (!oom_score_adjust_is_valid(value))
return -EINVAL;
xsprintf(t, "%i", value);
return write_string_file("/proc/self/oom_score_adj", t,
@@ -1857,69 +1925,19 @@ int get_oom_score_adjust(int *ret) {
delete_trailing_chars(t, WHITESPACE);
assert_se(safe_atoi(t, &a) >= 0);
assert_se(oom_score_adjust_is_valid(a));
r = safe_atoi(t, &a);
if (r < 0)
return r;
if (!oom_score_adjust_is_valid(a))
return -ENODATA;
if (ret)
*ret = a;
return 0;
}
int pidfd_get_pid(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;
char *p;
int r;
/* Converts a pidfd into a pid. Well known errors:
*
* -EBADF → fd invalid
* -ENOSYS → /proc/ not mounted
* -ENOTTY → fd valid, but not a pidfd
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
* -ESRCH → fd valid, but process is already reaped
*/
if (fd < 0)
return -EBADF;
xsprintf(path, "/proc/self/fdinfo/%i", fd);
r = read_full_virtual_file(path, &fdinfo, NULL);
if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */
return proc_mounted() > 0 ? -EBADF : -ENOSYS;
if (r < 0)
return r;
p = find_line_startswith(fdinfo, "Pid:");
if (!p)
return -ENOTTY; /* not a pidfd? */
p += strspn(p, WHITESPACE);
p[strcspn(p, WHITESPACE)] = 0;
if (streq(p, "0"))
return -EREMOTE; /* PID is in foreign PID namespace? */
if (streq(p, "-1"))
return -ESRCH; /* refers to reaped process? */
return parse_pid(p, ret);
}
int pidfd_verify_pid(int pidfd, pid_t pid) {
pid_t current_pid;
int r;
assert(pidfd >= 0);
assert(pid > 0);
r = pidfd_get_pid(pidfd, &current_pid);
if (r < 0)
return r;
return current_pid != pid ? -ESRCH : 0;
}
static int rlimit_to_nice(rlim_t limit) {
if (limit <= 1)
return PRIO_MAX-1; /* i.e. 19 */
@@ -1931,17 +1949,15 @@ static int rlimit_to_nice(rlim_t limit) {
}
int setpriority_closest(int priority) {
int current, limit, saved_errno;
struct rlimit highest;
int r, current, limit;
/* Try to set requested nice level */
if (setpriority(PRIO_PROCESS, 0, priority) >= 0)
r = RET_NERRNO(setpriority(PRIO_PROCESS, 0, priority));
if (r >= 0)
return 1;
/* Permission failed */
saved_errno = -errno;
if (!ERRNO_IS_PRIVILEGE(saved_errno))
return saved_errno;
if (!ERRNO_IS_NEG_PRIVILEGE(r))
return r;
errno = 0;
current = getpriority(PRIO_PROCESS, 0);
@@ -1955,24 +1971,21 @@ int setpriority_closest(int priority) {
* then the whole setpriority() system call is blocked to us, hence let's propagate the error
* right-away */
if (priority > current)
return saved_errno;
return r;
if (getrlimit(RLIMIT_NICE, &highest) < 0)
return -errno;
limit = rlimit_to_nice(highest.rlim_cur);
/* We are already less nice than limit allows us */
if (current < limit) {
log_debug("Cannot raise nice level, permissions and the resource limit do not allow it.");
return 0;
}
/* Push to the allowed limit */
/* Push to the allowed limit if we're higher than that. Note that we could also be less nice than
* limit allows us, but still higher than what's requested. In that case our current value is
* the best choice. */
if (current > limit)
if (setpriority(PRIO_PROCESS, 0, limit) < 0)
return -errno;
log_debug("Cannot set requested nice level (%i), used next best (%i).", priority, limit);
log_debug("Cannot set requested nice level (%i), using next best (%i).", priority, MIN(current, limit));
return 0;
}
@@ -1992,7 +2005,8 @@ _noreturn_ void freeze(void) {
break;
}
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
/* waitid() failed with an ECHLD error (because there are no left-over child processes) or any other
* (unexpected) error. Freeze for good now! */
for (;;)
pause();
}
@@ -2064,7 +2078,7 @@ int posix_spawn_wrapper(
const char *cgroup,
PidRef *ret_pidref) {
short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
short flags = POSIX_SPAWN_SETSIGMASK;
posix_spawnattr_t attr;
sigset_t mask;
int r;
@@ -2075,7 +2089,7 @@ int posix_spawn_wrapper(
* 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.
* if available.
* 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 */
@@ -2094,9 +2108,10 @@ int posix_spawn_wrapper(
_unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
#if HAVE_PIDFD_SPAWN
static bool have_clone_into_cgroup = true; /* kernel 5.7+ */
_cleanup_close_ int cgroup_fd = -EBADF;
if (cgroup) {
if (cgroup && have_clone_into_cgroup) {
_cleanup_free_ char *resolved_cgroup = NULL;
r = cg_get_path_and_check(
@@ -2130,25 +2145,38 @@ int posix_spawn_wrapper(
_cleanup_close_ int pidfd = -EBADF;
r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
if (r == 0) {
if (ERRNO_IS_NOT_SUPPORTED(r) && FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP) && cg_is_threaded(cgroup) > 0)
return -EUCLEAN; /* clone3() could also return EOPNOTSUPP if the target cgroup is in threaded mode,
turn that into something recognizable */
if ((ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r) || r == E2BIG) &&
FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP)) {
/* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but
* need to disable POSIX_SPAWN_SETCGROUP, which is what redirects to clone3().
* Note that we might get E2BIG here since some kernels (e.g. 5.4) support clone3()
* but not CLONE_INTO_CGROUP. */
/* CLONE_INTO_CGROUP definitely won't work, hence remember the fact so that we don't
* retry every time. */
have_clone_into_cgroup = false;
flags &= ~POSIX_SPAWN_SETCGROUP;
r = posix_spawnattr_setflags(&attr, flags);
if (r != 0)
return -r;
r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
}
if (r != 0)
return -r;
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
#else
pid_t pid;
r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
if (r != 0)
return -r;
@@ -2158,6 +2186,7 @@ int posix_spawn_wrapper(
return r;
return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */
#endif
}
int proc_dir_open(DIR **ret) {
@@ -2248,4 +2277,46 @@ static const char* const sched_policy_table[] = {
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
_noreturn_ void report_errno_and_exit(int errno_fd, int error) {
int r;
if (error >= 0)
_exit(EXIT_SUCCESS);
assert(errno_fd >= 0);
r = loop_write(errno_fd, &error, sizeof(error));
if (r < 0)
log_debug_errno(r, "Failed to write errno to errno_fd=%d: %m", errno_fd);
_exit(EXIT_FAILURE);
}
int read_errno(int errno_fd) {
int r;
assert(errno_fd >= 0);
/* The issue here is that it's impossible to distinguish between an error code returned by child and
* IO error arose when reading it. So, the function logs errors and return EIO for the later case. */
ssize_t n = loop_read(errno_fd, &r, sizeof(r), /* do_poll = */ false);
if (n < 0) {
log_debug_errno(n, "Failed to read errno: %m");
return -EIO;
}
if (n == sizeof(r)) {
if (r == 0)
return 0;
if (r < 0) /* child process reported an error, return it */
return log_debug_errno(r, "Child process failed with errno: %m");
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received an errno, but it's a positive value.");
}
if (n != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received unexpected amount of bytes while reading errno.");
/* the process exited without reporting an error, assuming success */
return 0;
}
#endif /* NM_IGNORED */

View File

@@ -14,7 +14,7 @@
#include "alloc-util.h"
#include "format-util.h"
#include "macro.h"
#include "namespace-util.h"
#include "pidref.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@@ -49,19 +49,18 @@ int get_process_exe(pid_t pid, char **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 pid_get_ppid(pid_t pid, pid_t *ret);
int pidref_get_ppid(const PidRef *pidref, pid_t *ret);
int pidref_get_ppid_as_pidref(const PidRef *pidref, PidRef *ret);
int pid_get_start_time(pid_t pid, usec_t *ret);
int pidref_get_start_time(const PidRef *pid, usec_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 {
@@ -91,10 +90,10 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value);
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 pidref_is_unwaited(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);
int pidref_is_my_child(PidRef *pidref);
int pidref_from_same_root_fs(PidRef *a, PidRef *b);
bool is_main_thread(void);
@@ -113,12 +112,12 @@ bool oom_score_adjust_is_valid(int oa);
#define OPINIONATED_PERSONALITY_MASK 0xFFUL
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
const char* personality_to_string(unsigned long);
int safe_personality(unsigned long p);
int opinionated_personality(unsigned long *ret);
const char *sigchld_code_to_string(int i) _const_;
const char* sigchld_code_to_string(int i) _const_;
int sigchld_code_from_string(const char *s) _pure_;
int sched_policy_to_string_alloc(int i, char **s);
@@ -149,9 +148,15 @@ static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
}
#define PID_AUTOMATIC ((pid_t) INT_MIN) /* special value indicating "acquire pid from connection peer" */
static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
static inline bool pid_is_automatic(pid_t p) {
return p == PID_AUTOMATIC;
}
#endif /* NM_IGNORED */
pid_t getpid_cached(void);
@@ -161,7 +166,7 @@ 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
/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, FORK_NEW_NETNS or FORK_NEW_PIDNS 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
@@ -176,18 +181,20 @@ typedef enum ForkFlags {
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! 💣 */
FORK_PACK_FDS = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */
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_CLOEXEC_OFF = 1 << 14, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
FORK_KEEP_NOTIFY_SOCKET = 1 << 15, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_DETACH = 1 << 16, /* Double fork if needed to ensure PID1/subreaper is parent */
FORK_PACK_FDS = 1 << 17, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */
FORK_NEW_MOUNTNS = 1 << 18, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_NEW_USERNS = 1 << 19, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_NEW_NETNS = 1 << 20, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_NEW_PIDNS = 1 << 21, /* Run child in its own PID namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_FREEZE = 1 << 22, /* Don't return in child, just call freeze() instead */
} ForkFlags;
int safe_fork_full(
@@ -245,9 +252,6 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
/* Like TAKE_PTR() but for pid_t, resetting them to 0 */
#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 0)
int pidfd_get_pid(int fd, pid_t *ret);
int pidfd_verify_pid(int pidfd, pid_t pid);
int setpriority_closest(int priority);
_noreturn_ void freeze(void);
@@ -267,3 +271,6 @@ int posix_spawn_wrapper(
int proc_dir_open(DIR **ret);
int proc_dir_read(DIR *d, pid_t *ret);
int proc_dir_read_pidref(DIR *d, PidRef *ret);
_noreturn_ void report_errno_and_exit(int errno_fd, int error);
int read_errno(int errno_fd);

View File

@@ -10,23 +10,22 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#if HAVE_SYS_AUXV_H
# include <sys/auxv.h>
#endif
#include "alloc-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "io-util.h"
#include "iovec-util.h"
#include "missing_random.h"
#include "missing_syscall.h"
#include "missing_threads.h"
#include "parse-util.h"
#include "pidfd-util.h"
#include "process-util.h"
#include "random-util.h"
#include "sha256.h"
@@ -43,10 +42,10 @@ static void fallback_random_bytes(void *p, size_t n) {
uint64_t call_id, block_id;
usec_t stamp_mono, stamp_real;
pid_t pid, tid;
uint64_t pidfdid;
uint8_t auxval[16];
} state = {
/* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
.label = "systemd fallback random bytes v1",
.call_id = fallback_counter++,
.stamp_mono = now(CLOCK_MONOTONIC),
.stamp_real = now(CLOCK_REALTIME),
@@ -54,9 +53,9 @@ static void fallback_random_bytes(void *p, size_t n) {
.tid = gettid(),
};
#if HAVE_SYS_AUXV_H
memcpy(state.label, "systemd fallback random bytes v1", sizeof(state.label));
memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval));
#endif
(void) pidfd_get_inode_id_self_cached(&state.pidfdid);
while (n > 0) {
struct sha256_ctx ctx;
@@ -77,8 +76,9 @@ static void fallback_random_bytes(void *p, size_t n) {
}
void random_bytes(void *p, size_t n) {
static bool have_getrandom = true, have_grndinsecure = true;
_cleanup_close_ int fd = -EBADF;
static bool have_grndinsecure = true;
assert(p || n == 0);
if (n == 0)
return;
@@ -86,32 +86,26 @@ void random_bytes(void *p, size_t n) {
for (;;) {
ssize_t l;
if (!have_getrandom)
break;
l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK);
if (l > 0) {
if (l < 0 && errno == EINVAL && have_grndinsecure) {
/* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
have_grndinsecure = false;
continue;
}
if (l <= 0)
break; /* Will block (with GRND_NONBLOCK), or unexpected error. Give up and fallback
to /dev/urandom. */
if ((size_t) l == n)
return; /* Done reading, success. */
p = (uint8_t *) p + l;
n -= l;
continue; /* Interrupted by a signal; keep going. */
} else if (l == 0)
break; /* Weird, so fallback to /dev/urandom. */
else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
have_getrandom = false;
break; /* No syscall, so fallback to /dev/urandom. */
} else if (errno == EINVAL && have_grndinsecure) {
have_grndinsecure = false;
continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
} else if (errno == EAGAIN && !have_grndinsecure)
break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */
break; /* Unexpected, so just give up and fallback to /dev/urandom. */
/* Interrupted by a signal; keep going. */
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0)
_cleanup_close_ int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd >= 0 && loop_read_exact(fd, p, n, false) >= 0)
return;
/* This is a terrible fallback. Oh well. */
@@ -119,8 +113,7 @@ void random_bytes(void *p, size_t n) {
}
int crypto_random_bytes(void *p, size_t n) {
static bool have_getrandom = true, seen_initialized = false;
_cleanup_close_ int fd = -EBADF;
assert(p || n == 0);
if (n == 0)
return 0;
@@ -128,42 +121,37 @@ int crypto_random_bytes(void *p, size_t n) {
for (;;) {
ssize_t l;
if (!have_getrandom)
break;
l = getrandom(p, n, 0);
if (l > 0) {
if (l < 0)
return -errno;
if (l == 0)
return -EIO; /* Weird, should never happen. */
if ((size_t) l == n)
return 0; /* Done reading, success. */
p = (uint8_t *) p + l;
n -= l;
continue; /* Interrupted by a signal; keep going. */
} else if (l == 0)
return -EIO; /* Weird, should never happen. */
else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
have_getrandom = false;
break; /* No syscall, so fallback to /dev/urandom. */
}
return -errno;
/* Interrupted by a signal; keep going. */
}
}
if (!seen_initialized) {
_cleanup_close_ int ready_fd = -EBADF;
int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret) {
_cleanup_free_ void *p = NULL;
int r;
ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (ready_fd < 0)
return -errno;
r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY);
assert(ret);
p = malloc(MAX(n, 1U));
if (!p)
return -ENOMEM;
r = crypto_random_bytes(p, n);
if (r < 0)
return r;
seen_initialized = true;
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return -errno;
return loop_read_exact(fd, p, n, false);
*ret = IOVEC_MAKE(TAKE_PTR(p), n);
return 0;
}
#if 0 /* NM_IGNORED */

View File

@@ -4,9 +4,11 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/uio.h>
void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */
int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret);
static inline uint64_t random_u64(void) {
uint64_t u;

View File

@@ -10,37 +10,37 @@
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
bool ratelimit_below(RateLimit *r) {
bool ratelimit_below(RateLimit *rl) {
usec_t ts;
assert(r);
assert(rl);
if (!ratelimit_configured(r))
if (!ratelimit_configured(rl))
return true;
ts = now(CLOCK_MONOTONIC);
if (r->begin <= 0 ||
usec_sub_unsigned(ts, r->begin) > r->interval) {
r->begin = ts; /* Start a new time window */
r->num = 1; /* Reset counter */
if (rl->begin <= 0 ||
usec_sub_unsigned(ts, rl->begin) > rl->interval) {
rl->begin = ts; /* Start a new time window */
rl->num = 1; /* Reset counter */
return true;
}
if (_unlikely_(r->num == UINT_MAX))
if (_unlikely_(rl->num == UINT_MAX))
return false;
r->num++;
return r->num <= r->burst;
rl->num++;
return rl->num <= rl->burst;
}
unsigned ratelimit_num_dropped(RateLimit *r) {
assert(r);
unsigned ratelimit_num_dropped(const RateLimit *rl) {
assert(rl);
if (r->num == UINT_MAX) /* overflow, return as special case */
if (rl->num == UINT_MAX) /* overflow, return as special case */
return UINT_MAX;
return LESS_BY(r->num, r->burst);
return LESS_BY(rl->num, rl->burst);
}
usec_t ratelimit_end(const RateLimit *rl) {

View File

@@ -18,13 +18,13 @@ static inline void ratelimit_reset(RateLimit *rl) {
rl->num = rl->begin = 0;
}
static inline bool ratelimit_configured(RateLimit *rl) {
static inline bool ratelimit_configured(const RateLimit *rl) {
return rl->interval > 0 && rl->burst > 0;
}
bool ratelimit_below(RateLimit *r);
bool ratelimit_below(RateLimit *rl);
unsigned ratelimit_num_dropped(RateLimit *r);
unsigned ratelimit_num_dropped(const RateLimit *rl);
usec_t ratelimit_end(const RateLimit *rl);
usec_t ratelimit_left(const RateLimit *rl);

View File

@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-shared.h"
#include <unistd.h>
#include "hexdecoct.h"
#include "macro.h"
#include "sha256.h"
int sha256_fd(int fd, uint64_t max_size, uint8_t ret[static SHA256_DIGEST_SIZE]) {
struct sha256_ctx ctx;
uint64_t total_size = 0;
sha256_init_ctx(&ctx);
for (;;) {
uint8_t buffer[64 * 1024];
ssize_t n;
n = read(fd, buffer, sizeof(buffer));
if (n < 0)
return -errno;
if (n == 0)
break;
if (!INC_SAFE(&total_size, n) || total_size > max_size)
return -EFBIG;
sha256_process_bytes(buffer, n, &ctx);
}
sha256_finish_ctx(&ctx, ret);
return 0;
}
int parse_sha256(const char *s, uint8_t ret[static SHA256_DIGEST_SIZE]) {
_cleanup_free_ uint8_t *data = NULL;
size_t size = 0;
int r;
if (!sha256_is_valid(s))
return -EINVAL;
r = unhexmem_full(s, SHA256_DIGEST_SIZE * 2, false, (void**) &data, &size);
if (r < 0)
return r;
assert(size == SHA256_DIGEST_SIZE);
memcpy(ret, data, size);
return 0;
}

View File

@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdint.h>
#include "sha256-fundamental.h"
#include "string-util.h"
int sha256_fd(int fd, uint64_t max_size, uint8_t ret[static SHA256_DIGEST_SIZE]);
int parse_sha256(const char *s, uint8_t res[static SHA256_DIGEST_SIZE]);
static inline bool sha256_is_valid(const char *s) {
return s && in_charset(s, HEXDIGITS) && (strlen(s) == SHA256_DIGEST_SIZE * 2);
}

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