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

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

View File

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

View File

@@ -19,9 +19,10 @@
#include "fd-util.h" #include "fd-util.h"
#include "socket-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 = { union sockaddr_union src = {
.in6.sin6_family = AF_INET6, .in6.sin6_family = AF_INET6,
.in6.sin6_addr = *ASSERT_PTR(local_address),
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
.in6.sin6_scope_id = ifindex, .in6.sin6_scope_id = ifindex,
}; };
@@ -29,9 +30,6 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
int r; int r;
assert(ifindex > 0); assert(ifindex > 0);
assert(local_address);
src.in6.sin6_addr = *local_address;
s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP); s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0) if (s < 0)
@@ -60,20 +58,14 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
return TAKE_FD(s); return TAKE_FD(s);
} }
int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
const void *packet, size_t len) {
union sockaddr_union dest = { union sockaddr_union dest = {
.in6.sin6_family = AF_INET6, .in6.sin6_family = AF_INET6,
.in6.sin6_addr = *ASSERT_PTR(server_address),
.in6.sin6_port = htobe16(DHCP6_PORT_SERVER), .in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
}; };
int r;
assert(server_address); if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)) < 0)
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)
return -errno; return -errno;
return 0; 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_RI:
case SD_DHCP6_OPTION_V6_DOTS_ADDRESS: case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF: case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
case SD_DHCP6_OPTION_V6_DNR:
return true; return true;
default: default:
return false; return false;
@@ -822,74 +823,6 @@ int dhcp6_option_parse_addresses(
return 0; 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) { int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
_cleanup_free_ char *domain = NULL; _cleanup_free_ char *domain = NULL;
int r; 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(optval || optlen == 0);
assert(ret); assert(ret);
r = parse_domain(&optval, &optlen, &domain); r = dns_name_from_wire_format(&optval, &optlen, &domain);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
@@ -924,11 +857,11 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha
while (optlen > 0) { while (optlen > 0) {
_cleanup_free_ char *name = NULL; _cleanup_free_ char *name = NULL;
r = parse_domain(&optval, &optlen, &name); r = dns_name_from_wire_format(&optval, &optlen, &name);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (dns_name_is_root(name)) /* root domain */
continue; return -EBADMSG;
r = strv_consume(&names, TAKE_PTR(name)); r = strv_consume(&names, TAKE_PTR(name));
if (r < 0) if (r < 0)

View File

@@ -28,9 +28,11 @@ typedef struct DHCP6Message DHCP6Message;
#define DHCP6_MIN_OPTIONS_SIZE \ #define DHCP6_MIN_OPTIONS_SIZE \
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ #define IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ ((const struct in6_addr) { { { \
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, \
} } } )
enum { enum {
DHCP6_PORT_SERVER = 547, DHCP6_PORT_SERVER = 547,

View File

@@ -3,7 +3,7 @@
#include "nm-sd-adapt-core.h" #include "nm-sd-adapt-core.h"
#include "env-util.h" #include "env-util.h"
#include "format-util.h" #include "format-ifname.h"
#include "network-common.h" #include "network-common.h"
#include "socket-util.h" #include "socket-util.h"
#include "unaligned.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) { int dhcp6_client_send_message(sd_dhcp6_client *client) {
_cleanup_free_ uint8_t *buf = NULL; _cleanup_free_ uint8_t *buf = NULL;
struct in6_addr all_servers =
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
struct sd_dhcp6_option *j; struct sd_dhcp6_option *j;
usec_t elapsed_usec, time_now; usec_t elapsed_usec, time_now;
be16_t elapsed_time; be16_t elapsed_time;
@@ -845,7 +843,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0) if (r < 0)
return r; 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) if (r < 0)
return r; return r;
@@ -1336,7 +1334,7 @@ static int client_receive_message(
return 0; return 0;
} }
if ((size_t) len < sizeof(DHCP6Message)) { 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; return 0;
} }
@@ -1412,7 +1410,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
r = client_send_release(client); r = client_send_release(client);
if (r < 0) if (r < 0)
log_dhcp6_client_errno(client, r, 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); 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) { int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
assert_return(client, -EINVAL); if (!client)
return false;
return client->state != DHCP6_STATE_STOPPED; return client->state != DHCP6_STATE_STOPPED;
} }

View File

@@ -10,7 +10,9 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "dhcp6-internal.h" #include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h" #include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "network-common.h" #include "network-common.h"
#include "sort-util.h"
#include "strv.h" #include "strv.h"
#include "unaligned.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) if (r < 0)
return r; 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) { 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); 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 dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r; int r;
@@ -765,8 +866,7 @@ static int dhcp6_lease_parse_message(
continue; continue;
} }
dhcp6_ia_free(lease->ia_na); free_and_replace_full(lease->ia_na, ia, dhcp6_ia_free);
lease->ia_na = TAKE_PTR(ia);
break; break;
} }
case SD_DHCP6_OPTION_IA_PD: { case SD_DHCP6_OPTION_IA_PD: {
@@ -790,8 +890,7 @@ static int dhcp6_lease_parse_message(
continue; continue;
} }
dhcp6_ia_free(lease->ia_pd); free_and_replace_full(lease->ia_pd, ia, dhcp6_ia_free);
lease->ia_pd = TAKE_PTR(ia);
break; break;
} }
case SD_DHCP6_OPTION_RAPID_COMMIT: 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); irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
break; 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: case SD_DHCP6_OPTION_VENDOR_OPTS:
r = dhcp6_lease_add_vendor_option(lease, optval, optlen); r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
if (r < 0) 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_na);
dhcp6_ia_free(lease->ia_pd); dhcp6_ia_free(lease->ia_pd);
free(lease->dns); free(lease->dns);
#if 0 /* NM_IGNORED */
dns_resolver_done_many(lease->dnr, lease->n_dnr);
#endif /* NM_IGNORED */
free(lease->fqdn); free(lease->fqdn);
free(lease->captive_portal); free(lease->captive_portal);
strv_free(lease->domains); 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(device);
assert(tag); assert(tag);
r = device_get_device_id(device, &id); r = sd_device_get_device_id(device, &id);
if (r < 0) if (r < 0)
return r; return r;
@@ -801,7 +801,7 @@ static int device_get_db_path(sd_device *device, char **ret) {
assert(device); assert(device);
assert(ret); assert(ret);
r = device_get_device_id(device, &id); r = sd_device_get_device_id(device, &id);
if (r < 0) if (r < 0)
return r; 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_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret); 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_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_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_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_devlink_priority(sd_device *device, int *ret);
int device_get_devnode_mode(sd_device *device, mode_t *ret); int device_get_devnode_mode(sd_device *device, mode_t *ret);
int device_get_devnode_uid(sd_device *device, uid_t *ret); int device_get_devnode_uid(sd_device *device, uid_t *ret);

View File

@@ -100,7 +100,7 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
assert(st); assert(st);
return devname_from_devnum(st->st_mode, st->st_rdev, ret); 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); 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); r = path_simplify_alloc(_syspath, &syspath);
if (r < 0) if (r < 0)
return r; return log_oom_debug();
} }
assert_se(devpath = startswith(syspath, "/sys")); 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(ret, -EINVAL);
assert_return(ifname, -EINVAL); assert_return(ifname, -EINVAL);
if (ifname_valid(ifname)) {
r = device_new_from_main_ifname(ret, ifname); r = device_new_from_main_ifname(ret, ifname);
if (r >= 0) if (r >= 0)
return r; return r;
}
r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL); r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL);
if (r < 0) if (r < 0)
@@ -393,32 +395,87 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
return 0; 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 *a,
const char *b, const char *b,
const char *c, const char *c,
const char *d, const char *d) {
sd_device **ret) {
const char *p; _cleanup_(sd_device_unrefp) sd_device *new_device = NULL;
_cleanup_free_ char *p = NULL;
int r; int r;
p = strjoina(a, b, c, d); assert(device);
if (access(p, F_OK) < 0) assert(sysname);
return IN_SET(errno, ENOENT, ENAMETOOLONG) ? 0 : -errno; /* If this sysfs is too long then it doesn't exist either */
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) if (r < 0)
return r; 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; 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( _public_ int sd_device_new_from_subsystem_sysname(
sd_device **ret, sd_device **ret,
const char *subsystem, const char *subsystem,
const char *sysname) { const char *sysname) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
char *name; char *name;
int r; int r;
@@ -437,19 +494,15 @@ _public_ int sd_device_new_from_subsystem_sysname(
if (streq(subsystem, "subsystem")) { if (streq(subsystem, "subsystem")) {
FOREACH_STRING(s, "/sys/bus/", "/sys/class/") { 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) if (r < 0)
return r; return r;
if (r > 0)
return 0;
} }
} else if (streq(subsystem, "module")) { } 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) if (r < 0)
return r; return r;
if (r > 0)
return 0;
} else if (streq(subsystem, "drivers")) { } else if (streq(subsystem, "drivers")) {
const char *sep; const char *sep;
@@ -461,35 +514,33 @@ _public_ int sd_device_new_from_subsystem_sysname(
sep++; sep++;
if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */ 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 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) if (r < 0)
return r; 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) if (r < 0)
return r; 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) if (r < 0)
return r; 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) if (r < 0)
return r; return r;
if (r > 0)
return 0;
if (!device)
return -ENODEV; return -ENODEV;
*ret = TAKE_PTR(device);
return 0;
} }
_public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) { _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); 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) { static int device_new_from_devname(sd_device **ret, const char *devname, bool strict) {
struct stat st; int r;
dev_t devnum;
mode_t mode;
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(devname, -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 /* This function actually accepts both devlinks and devnames, i.e. both symlinks and device
* nodes below /dev/. */ * nodes below /dev/. */
/* Also ignore when the specified path is "/dev". */ if (strict && isempty(path_startswith(devname, "/dev/")))
if (isempty(path_startswith(devname, "/dev")))
return -EINVAL; return -EINVAL;
dev_t devnum;
mode_t mode;
if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0) 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. /* 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 * 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. */ * that the specified path exists. So, this works fine without udevd being running. */
return device_new_from_mode_and_devnum(ret, mode, devnum); return device_new_from_mode_and_devnum(ret, mode, devnum);
if (stat(devname, &st) < 0) _cleanup_free_ char *resolved = NULL;
return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno; 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); 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) { _public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(path, -EINVAL); assert_return(path, -EINVAL);
if (path_startswith(path, "/dev")) if (device_new_from_devname(ret, path, /* strict = */ false) >= 0)
return sd_device_new_from_devname(ret, path); return 0;
return device_new_from_syspath(ret, path, /* strict = */ false); 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 */ #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) { _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
int r; int r;
@@ -1226,7 +1302,7 @@ _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
if (devtype) if (devtype)
*devtype = device->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) { _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) { _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
assert(device); assert_return(device, -EINVAL);
assert(ret);
if (!device->device_id) { if (!device->device_id) {
_cleanup_free_ char *id = NULL; _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); device->device_id = TAKE_PTR(id);
} }
if (ret)
*ret = device->device_id; *ret = device->device_id;
return 0; return 0;
} }
@@ -2428,7 +2504,7 @@ int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_valu
return v > 0; 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; const char *value;
int r; int r;
@@ -2437,7 +2513,7 @@ int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned
return r; return r;
unsigned v; unsigned v;
r = safe_atou(value, &v); r = safe_atou_full(value, base, &v);
if (r < 0) if (r < 0)
return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr); return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);

View File

@@ -189,6 +189,9 @@ struct inode_data {
* iteration. */ * iteration. */
int fd; int fd;
/* The path that the fd points to. The field is optional. */
char *path;
/* The inotify "watch descriptor" */ /* The inotify "watch descriptor" */
int wd; 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); 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 */ #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 */ #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); 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 */ #endif /* NM_IGNORED */

View File

@@ -27,9 +27,11 @@
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h" #include "missing_threads.h"
#include "missing_wait.h"
#include "origin-id.h" #include "origin-id.h"
#include "path-util.h" #include "path-util.h"
#include "prioq.h" #include "prioq.h"
#include "pidfd-util.h"
#include "process-util.h" #include "process-util.h"
#include "psi-util.h" #include "psi-util.h"
#include "set.h" #include "set.h"
@@ -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); _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) { _public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
if (s) int r;
(void) sd_event_source_set_enabled(s, SD_EVENT_OFF);
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); 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) { static sd_event_source* source_free(sd_event_source *s) {
int r;
assert(s); assert(s);
source_disconnect(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.process_owned) {
if (!s->child.exited) { if (!s->child.exited) {
bool sent = false; if (s->child.pidfd >= 0)
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
if (s->child.pidfd >= 0) { else
if (pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0) < 0) { r = RET_NERRNO(kill(s->child.pid, SIGKILL));
if (errno == ESRCH) /* Already dead */ if (r < 0 && r != -ESRCH)
sent = true; log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
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",
s->child.pid); s->child.pid);
} }
@@ -1109,6 +1107,9 @@ static sd_event_source* source_free(sd_event_source *s) {
siginfo_t si = {}; siginfo_t si = {};
/* Reap the child if we can */ /* 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); (void) waitid(P_PID, s->child.pid, &si, WEXITED);
} }
} }
@@ -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)); 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( _public_ int sd_event_add_child(
sd_event *e, sd_event *e,
sd_event_source **ret, sd_event_source **ret,
@@ -1629,34 +1625,29 @@ _public_ int sd_event_add_child(
if (!s) if (!s)
return -ENOMEM; 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->wakeup = WAKEUP_EVENT_SOURCE;
s->child.options = options; s->child.options = options;
s->child.callback = callback; s->child.callback = callback;
s->userdata = userdata; s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT; 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)) { 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); r = source_child_pidfd_register(s, s->enabled);
if (r < 0) if (r < 0)
return r; return r;
} else { } 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); r = event_make_signal_data(e, SIGCHLD, NULL);
if (r < 0) if (r < 0)
return r; return r;
@@ -1686,7 +1677,6 @@ _public_ int sd_event_add_child_pidfd(
sd_event_child_handler_t callback, sd_event_child_handler_t callback,
void *userdata) { void *userdata) {
_cleanup_(source_freep) sd_event_source *s = NULL; _cleanup_(source_freep) sd_event_source *s = NULL;
pid_t pid; pid_t pid;
int r; int r;
@@ -1727,17 +1717,12 @@ _public_ int sd_event_add_child_pidfd(
s->wakeup = WAKEUP_EVENT_SOURCE; s->wakeup = WAKEUP_EVENT_SOURCE;
s->child.pidfd = pidfd; s->child.pidfd = pidfd;
s->child.pid = pid;
s->child.options = options; s->child.options = options;
s->child.callback = callback; 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->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->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT; 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)) { if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We only want to watch for WEXITED */ /* We only want to watch for WEXITED */
r = source_child_pidfd_register(s, s->enabled); r = source_child_pidfd_register(s, s->enabled);
@@ -1752,6 +1737,11 @@ _public_ int sd_event_add_child_pidfd(
e->need_process_child = true; 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++; e->n_online_child_sources++;
if (ret) if (ret)
@@ -2280,6 +2270,7 @@ static void event_free_inode_data(
assert_se(hashmap_remove(d->inotify_data->inodes, d) == d); assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
} }
free(d->path);
free(d); 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); wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
if (wd < 0) if (wd < 0)
return -errno; return wd;
if (d->wd < 0) { if (d->wd < 0) {
r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d); 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; return 1;
} }
#if 0 /* NM_IGNORED */
static int inotify_exit_callback(sd_event_source *s, const struct inotify_event *event, void *userdata) { static int inotify_exit_callback(sd_event_source *s, const struct inotify_event *event, void *userdata) {
assert(s); 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); 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 */ /* Link our event source to the inode data object */
@@ -2579,6 +2580,7 @@ _public_ int sd_event_add_inotify(
return r; return r;
} }
#endif /* NM_IGNORED */
static sd_event_source* event_source_free(sd_event_source *s) { static sd_event_source* event_source_free(sd_event_source *s) {
if (!s) if (!s)
@@ -2609,14 +2611,14 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des
return free_and_strdup(&s->description, description); 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(s, -EINVAL);
assert_return(description, -EINVAL); assert_return(ret, -EINVAL);
if (!s->description) if (!s->description)
return -ENXIO; return -ENXIO;
*description = s->description; *ret = s->description;
return 0; return 0;
} }
@@ -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) { _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(s, -EINVAL);
assert_return(fd >= 0, -EBADF); 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) if (s->io.fd == fd)
return 0; 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; saved_fd = s->io.fd;
assert(s->io.registered);
s->io.fd = fd; s->io.fd = fd;
assert(event_source_is_offline(s) == !s->io.registered);
if (s->io.registered) {
s->io.registered = false; s->io.registered = false;
r = source_io_register(s, s->enabled, s->io.events); 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); (void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
} }
if (s->io.owned)
safe_close(saved_fd);
return 0; return 0;
} }
@@ -2697,13 +2698,13 @@ _public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
return 0; 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(s, -EINVAL);
assert_return(events, -EINVAL); assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM); assert_return(s->type == SOURCE_IO, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
*events = s->io.events; *ret = s->io.events;
return 0; return 0;
} }
@@ -2735,14 +2736,14 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events)
return 0; 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(s, -EINVAL);
assert_return(revents, -EINVAL); assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM); assert_return(s->type == SOURCE_IO, -EDOM);
assert_return(s->pending, -ENODATA); assert_return(s->pending, -ENODATA);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
*revents = s->io.revents; *ret = s->io.revents;
return 0; return 0;
} }
@@ -2754,11 +2755,12 @@ _public_ int sd_event_source_get_signal(sd_event_source *s) {
return s->signal.sig; 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(s, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
*priority = s->priority; *ret = s->priority;
return 0; 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); 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 */ /* 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; 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(s, -EINVAL);
assert_return(usec, -EINVAL); assert_return(ret, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
*usec = s->time.next; *ret = s->time.next;
return 0; 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); 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(s, -EINVAL);
assert_return(usec, -EINVAL); assert_return(ret, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
*usec = s->time.accuracy; *ret = s->time.accuracy;
return 0; return 0;
} }
@@ -3174,23 +3183,23 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec
return 0; 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(s, -EINVAL);
assert_return(clock, -EINVAL); assert_return(ret, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); 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; 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(s, -EINVAL);
assert_return(pid, -EINVAL); assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM); assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
*pid = s->child.pid; *ret = s->child.pid;
return 0; return 0;
} }
@@ -3225,11 +3234,9 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
if (si) if (si)
copy = *si; copy = *si;
if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0) { 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))
return -errno; return -errno;
} else
return 0; return 0;
} }
@@ -3290,13 +3297,29 @@ _public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own)
return 0; 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(s, -EINVAL);
assert_return(mask, -EINVAL); assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_INOTIFY, -EDOM); assert_return(s->type == SOURCE_INOTIFY, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); 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; 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))) if (_unlikely_(n != sizeof(si)))
return -EIO; return -EIO;
assert(SIGNAL_VALID(si.ssi_signo)); if (_unlikely_(!SIGNAL_VALID(si.ssi_signo)))
return -EIO;
if (e->signal_sources) if (e->signal_sources)
s = e->signal_sources[si.ssi_signo]; 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 /* 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 * 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 * 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 * biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's
* missing. */ * missing. */
@@ -4825,13 +4849,13 @@ _public_ int sd_event_dispatch(sd_event *e) {
static void event_log_delays(sd_event *e) { static void event_log_delays(sd_event *e) {
char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p; char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p;
size_t l, i; size_t l;
p = b; p = b;
l = sizeof(b); l = sizeof(b);
for (i = 0; i < ELEMENTSOF(e->delays); i++) { FOREACH_ELEMENT(delay, e->delays) {
l = strpcpyf(&p, l, "%u ", e->delays[i]); l = strpcpyf(&p, l, "%u ", *delay);
e->delays[i] = 0; *delay = 0;
} }
log_debug("Event loop iterations: %s", b); 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(!event_origin_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
PROTECT_EVENT(e); PROTECT_EVENT(e);
while (e->state != SD_EVENT_FINISHED) { while (e->state != SD_EVENT_FINISHED) {
@@ -4920,7 +4943,7 @@ _public_ int sd_event_get_state(sd_event *e) {
return e->state; 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, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG); assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_origin_changed(e), -ECHILD); 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) if (!e->exit_requested)
return -ENODATA; return -ENODATA;
if (code) if (ret)
*code = e->exit_code; *ret = e->exit_code;
return 0; return 0;
} }
@@ -4945,10 +4968,10 @@ _public_ int sd_event_exit(sd_event *e, int code) {
return 0; 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, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG); assert_return(e = event_resolve(e), -ENOPKG);
assert_return(usec, -EINVAL); assert_return(ret, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD); assert_return(!event_origin_changed(e), -ECHILD);
if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) 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)) { if (!triple_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran before and thus have no cached time. */ /* Implicitly fall back to now() if we never ran before and thus have no cached time. */
*usec = now(clock); *ret = now(clock);
return 1; return 1;
} }
*usec = triple_timestamp_by_clock(&e->timestamp, clock); *ret = triple_timestamp_by_clock(&e->timestamp, clock);
return 0; return 0;
} }
@@ -4989,18 +5012,17 @@ _public_ int sd_event_default(sd_event **ret) {
} }
#if 0 /* NM_IGNORED */ #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, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG); assert_return(e = event_resolve(e), -ENOPKG);
assert_return(tid, -EINVAL); assert_return(ret, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD); assert_return(!event_origin_changed(e), -ECHILD);
if (e->tid != 0) { if (e->tid == 0)
*tid = e->tid;
return 0;
}
return -ENXIO; return -ENXIO;
*ret = e->tid;
return 0;
} }
_public_ int sd_event_set_watchdog(sd_event *e, int b) { _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; int r;
assert_return(e, -EINVAL); 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) { if (b) {
/* We want to maintain pointers to these event sources, so that we can destroy them when told /* 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) if (r < 0)
return r; 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; 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); r = sd_event_add_signal(e, &e->sigterm_event_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0) { if (r < 0) {
if (change) { 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); e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
} }
return r; 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; change = true;
} }
} else { } else {
if (e->sigint_event_source) { 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); e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
change = true; change = true;
} }
if (e->sigterm_event_source) { 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); e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
change = true; change = true;
} }

View File

@@ -11,6 +11,8 @@
#include "hexdecoct.h" #include "hexdecoct.h"
#include "id128-util.h" #include "id128-util.h"
#include "io-util.h" #include "io-util.h"
#include "namespace-util.h"
#include "process-util.h"
#include "sha256.h" #include "sha256.h"
#include "stdio-util.h" #include "stdio-util.h"
#include "string-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); 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 */ #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); 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" */ /* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \ #define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
IN_SET(r, \ IN_SET(r, \

View File

@@ -15,6 +15,7 @@
#include "hmac.h" #include "hmac.h"
#include "id128-util.h" #include "id128-util.h"
#include "io-util.h" #include "io-util.h"
#include "keyring-util.h"
#include "macro.h" #include "macro.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h" #include "missing_threads.h"
@@ -176,14 +177,24 @@ int id128_get_machine(const char *root, sd_id128_t *ret) {
} }
#endif /* NM_IGNORED */ #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) { _public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id = {}; static thread_local sd_id128_t saved_boot_id = {};
int r; int r;
if (sd_id128_is_null(saved_boot_id)) { 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); r = id128_get_boot(&saved_boot_id);
if (r == -ENOENT && proc_mounted() == 0)
return -ENOSYS;
if (r < 0) if (r < 0)
return r; return r;
} }
@@ -199,7 +210,6 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
char *d, *p, *g, *u, *e; char *d, *p, *g, *u, *e;
unsigned long perms; unsigned long perms;
key_serial_t key; key_serial_t key;
size_t sz = 256;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
int r, c; int r, c;
@@ -218,24 +228,9 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
return -errno; return -errno;
} }
for (;;) { r = keyring_describe(key, &description);
description = new(char, sz); if (r < 0)
if (!description) return r;
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);
/* Chop off the final description string */ /* Chop off the final description string */
d = strrchr(description, ';'); 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); 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 */ #endif /* NM_IGNORED */

View File

@@ -45,6 +45,10 @@ typedef void (*_sd_destroy_t)(void *userdata);
# define _sd_pure_ __attribute__((__pure__)) # define _sd_pure_ __attribute__((__pure__))
#endif #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 /* 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 * 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 * (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_MIN = INT64_MIN, \
_SD_##id##_INT64_MAX = INT64_MAX _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 #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_enumerator sd_device_enumerator;
typedef struct sd_device_monitor sd_device_monitor; 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_ADD,
SD_DEVICE_REMOVE, SD_DEVICE_REMOVE,
SD_DEVICE_CHANGE, 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_syspath(sd_device *device, const char **ret);
int sd_device_get_subsystem(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_devtype(sd_device *device, const char **ret);
int sd_device_get_devnum(sd_device *device, dev_t *devnum); int sd_device_get_devnum(sd_device *device, dev_t *devnum);
int sd_device_get_ifindex(sd_device *device, int *ifindex); int sd_device_get_ifindex(sd_device *device, int *ifindex);
@@ -85,6 +86,7 @@ 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_action(sd_device *device, sd_device_action_t *ret);
int sd_device_get_seqnum(sd_device *device, uint64_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_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_is_initialized(sd_device *device);
int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret); int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret);
@@ -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_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_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator);
/* device monitor */ /* 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_ref(sd_device_monitor *m);
sd_device_monitor *sd_device_monitor_unref(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_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_attach_event(sd_device_monitor *m, sd_event *event);
int sd_device_monitor_detach_event(sd_device_monitor *m); 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); 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_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_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_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_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_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype);
int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag); int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag);

View File

@@ -30,6 +30,7 @@
_SD_BEGIN_DECLARATIONS; _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease; 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_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret); int sd_dhcp6_lease_get_t1(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_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_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_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_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret); int sd_dhcp6_lease_get_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_SLAP_QUAD = 140, /* RFC 8948 */
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */ SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */ SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */ SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143, /* RFC 6153 */
/* option codes 144-65535 are unassigned */ SD_DHCP6_OPTION_V6_DNR = 144 /* RFC 9463 */
/* option codes 145-65535 are unassigned */
}; };
_SD_END_DECLARATIONS; _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_loop(sd_event *e);
int sd_event_exit(sd_event *e, int code); 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_fd(sd_event *e);
int sd_event_get_state(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_tid(sd_event *e, pid_t *ret);
int sd_event_get_exit_code(sd_event *e, int *code); int sd_event_get_exit_code(sd_event *e, int *ret);
int sd_event_set_watchdog(sd_event *e, int b); int sd_event_set_watchdog(sd_event *e, int b);
int sd_event_get_watchdog(sd_event *e); int sd_event_get_watchdog(sd_event *e);
int sd_event_get_iteration(sd_event *e, uint64_t *ret); int sd_event_get_iteration(sd_event *e, uint64_t *ret);
@@ -128,28 +128,28 @@ void* sd_event_source_get_userdata(sd_event_source *s);
void* sd_event_source_set_userdata(sd_event_source *s, void *userdata); 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_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_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_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_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_set_enabled(sd_event_source *s, int enabled);
int sd_event_source_get_io_fd(sd_event_source *s); 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_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_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_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_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_io_revents(sd_event_source *s, uint32_t *ret);
int sd_event_source_get_time(sd_event_source *s, uint64_t *usec); 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(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_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_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_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(sd_event_source *s);
int sd_event_source_get_child_pidfd_own(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); 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); int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
#endif #endif
int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); 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_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_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); int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);

View File

@@ -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_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_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_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) \ #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, \ { .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) \ #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 #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]; 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); 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; 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); 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_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }}) #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 (;;) { for (;;) {
sd_id128_t b = va_arg(ap, sd_id128_t); 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; va_list ap;
int r; int r;

View File

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

View File

@@ -5,6 +5,7 @@ libnm_systemd_shared = static_library(
sources: files( sources: files(
'nm-sd-utils-shared.c', 'nm-sd-utils-shared.c',
'src/basic/alloc-util.c', 'src/basic/alloc-util.c',
'src/basic/chattr-util.c',
'src/basic/btrfs.c', 'src/basic/btrfs.c',
'src/basic/devnum-util.c', 'src/basic/devnum-util.c',
'src/basic/env-file.c', 'src/basic/env-file.c',
@@ -14,6 +15,7 @@ libnm_systemd_shared = static_library(
'src/basic/extract-word.c', 'src/basic/extract-word.c',
'src/basic/fd-util.c', 'src/basic/fd-util.c',
'src/basic/fileio.c', 'src/basic/fileio.c',
'src/basic/format-ifname.c',
'src/basic/format-util.c', 'src/basic/format-util.c',
'src/basic/fs-util.c', 'src/basic/fs-util.c',
'src/basic/glyph-util.c', 'src/basic/glyph-util.c',
@@ -31,10 +33,12 @@ libnm_systemd_shared = static_library(
'src/basic/ordered-set.c', 'src/basic/ordered-set.c',
'src/basic/parse-util.c', 'src/basic/parse-util.c',
'src/basic/path-util.c', 'src/basic/path-util.c',
'src/basic/pidfd-util.c',
'src/basic/prioq.c', 'src/basic/prioq.c',
'src/basic/process-util.c', 'src/basic/process-util.c',
'src/basic/random-util.c', 'src/basic/random-util.c',
'src/basic/ratelimit.c', 'src/basic/ratelimit.c',
'src/basic/sha256.c',
'src/basic/signal-util.c', 'src/basic/signal-util.c',
'src/basic/socket-util.c', 'src/basic/socket-util.c',
'src/basic/stat-util.c', 'src/basic/stat-util.c',
@@ -45,7 +49,7 @@ libnm_systemd_shared = static_library(
'src/basic/time-util.c', 'src/basic/time-util.c',
'src/basic/tmpfile-util.c', 'src/basic/tmpfile-util.c',
'src/basic/utf8.c', 'src/basic/utf8.c',
'src/fundamental/sha256.c', 'src/fundamental/sha256-fundamental.c',
'src/fundamental/string-util-fundamental.c', 'src/fundamental/string-util-fundamental.c',
'src/shared/dns-domain.c', 'src/shared/dns-domain.c',
'src/shared/web-util.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 #define ALTIFNAMSIZ 128
#endif #endif
/* kernel cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
#ifndef PID_FS_MAGIC
#define PID_FS_MAGIC 0x50494446
#endif
#define HAVE_LINUX_TIME_TYPES_H 0 #define HAVE_LINUX_TIME_TYPES_H 0
#ifndef __COMPAR_FN_T #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 need,
size_t size) { size_t size) {
size_t a, newalloc; size_t newalloc;
void *q; void *q;
assert(p); assert(p);
@@ -62,14 +62,13 @@ void* greedy_realloc(
return NULL; return NULL;
newalloc = need * 2; newalloc = need * 2;
if (size_multiply_overflow(newalloc, size)) if (!MUL_ASSIGN_SAFE(&newalloc, size))
return NULL; return NULL;
a = newalloc * size;
if (a < 64) /* Allocate at least 64 bytes */ if (newalloc < 64) /* Allocate at least 64 bytes */
a = 64; newalloc = 64;
q = realloc(*p, a); q = realloc(*p, newalloc);
if (!q) if (!q)
return NULL; return NULL;

View File

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

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. * generate paths with multiple adjacent / removed.
*/ */
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret); int cg_path_open(const char *controller, const char *path);
int cg_read_pid(FILE *f, pid_t *ret); int cg_cgroupid_open(int fsfd, uint64_t id);
int cg_read_pidref(FILE *f, PidRef *ret);
int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret); int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret);
int cg_read_subgroup(DIR *d, 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 { typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0, CGROUP_SIGCONT = 1 << 0,
CGROUP_IGNORE_SELF = 1 << 1, CGROUP_IGNORE_SELF = 1 << 1,
CGROUP_REMOVE = 1 << 2, CGROUP_DONT_SKIP_UNMAPPED = 1 << 2,
CGROUP_NO_PIDFD = 1 << 3,
} CGroupFlags; } 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); 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); 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_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_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_threaded(const char *path);
int cg_is_delegated(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_get_xattr_bool(const char *path, const char *name);
int cg_remove_xattr(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(const char *controller, const char *path);
int cg_is_empty_recursive(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_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_session(const char *path, char **ret_session);
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid); int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
int cg_path_get_unit(const char *path, char **ret_unit); int cg_path_get_unit(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_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_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_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_pid_get_unit(pid_t pid, char **ret_unit);
int cg_pidref_get_unit(const PidRef *pidref, char **ret); int cg_pidref_get_unit(const PidRef *pidref, char **ret);
int cg_pid_get_user_unit(pid_t pid, char **ret_unit); int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
@@ -298,8 +307,6 @@ bool cg_controller_is_valid(const char *p);
int cg_slice_to_path(const char *unit, char **ret); 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(CGroupMask *ret);
int cg_mask_supported_subtree(const char *root, CGroupMask *ret); int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
int cg_mask_from_string(const char *s, 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)]; uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
} cg_file_handle; } 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) #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. * also points to the result path even if this flag is set.
* When this specified, chase() will succeed with 1 even if the * When this specified, chase() will succeed with 1 even if the
* file points to the last path component does not exist. */ * 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 CHASE_MKDIR_0755 = 1 << 11, /* Create any missing directories in the given path. */
* 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_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved 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; } ChaseFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b); 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_INTERVAL (10*USEC_PER_SEC)
#define DEFAULT_START_LIMIT_BURST 5 #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 /* The default time after which exit-on-idle services exit. This
* should be kept lower than the watchdog timeout, because otherwise * should be kept lower than the watchdog timeout, because otherwise
* the watchdog pings will keep the loop busy. */ * the watchdog pings will keep the loop busy. */
@@ -67,18 +64,12 @@
"/usr/local/lib/" n "\0" \ "/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" "/usr/lib/" n "\0"
#define CONF_PATHS_USR(n) \ #define CONF_PATHS(n) \
"/etc/" n, \ "/etc/" n, \
"/run/" n, \ "/run/" n, \
"/usr/local/lib/" n, \ "/usr/local/lib/" n, \
"/usr/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) \ #define CONF_PATHS_STRV(n) \
STRV_MAKE(CONF_PATHS(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. */ /* 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 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) { int device_path_make_inaccessible(mode_t mode, char **ret) {
char *s; const char *s;
assert(ret); assert(ret);
if (S_ISCHR(mode)) if (S_ISCHR(mode))
s = strdup("/run/systemd/inaccessible/chr"); s = "/run/systemd/inaccessible/chr";
else if (S_ISBLK(mode)) else if (S_ISBLK(mode))
s = strdup("/run/systemd/inaccessible/blk"); s = "/run/systemd/inaccessible/blk";
else else
return -ENODEV; return -ENODEV;
if (!s)
return -ENOMEM;
*ret = s; return strdup_to(ret, s);
return 0;
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */

View File

@@ -20,6 +20,7 @@
#include "stdio-util.h" #include "stdio-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "syslog-util.h"
#include "utf8.h" #include "utf8.h"
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
@@ -568,6 +569,34 @@ char *strv_env_pairs_get(char **l, const char *name) {
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
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) { char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
int k = 0; int k = 0;
@@ -800,10 +829,10 @@ int replace_env_full(
t = v; 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) if (r < 0)
return r; 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) if (r < 0)
return r; return r;
@@ -935,21 +964,21 @@ int replace_env_argv(
return r; return r;
n[++k] = NULL; 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) if (r < 0)
return r; 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) if (r < 0)
return r; return r;
} }
if (ret_unset_variables) { if (ret_unset_variables) {
strv_uniq(strv_sort(unset_variables)); strv_sort_uniq(unset_variables);
*ret_unset_variables = TAKE_PTR(unset_variables); *ret_unset_variables = TAKE_PTR(unset_variables);
} }
if (ret_bad_variables) { if (ret_bad_variables) {
strv_uniq(strv_sort(bad_variables)); strv_sort_uniq(bad_variables);
*ret_bad_variables = TAKE_PTR(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)); return RET_NERRNO(unsetenv(name));
va_start(ap, valuef); va_start(ap, valuef);
DISABLE_WARNING_FORMAT_NONLITERAL;
r = vasprintf(&value, valuef, ap); r = vasprintf(&value, valuef, ap);
REENABLE_WARNING;
va_end(ap); va_end(ap);
if (r < 0) if (r < 0)
return -ENOMEM; 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)); return RET_NERRNO(setenv(name, value, overwrite));
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View File

@@ -60,6 +60,7 @@ static inline char* strv_env_get(char * const *x, const char *n) {
} }
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 getenv_bool(const char *p);
int secure_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); _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) { static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
return IN_SET(r, return IN_SET(r,
-EOPNOTSUPP, -EOPNOTSUPP,
@@ -167,10 +167,17 @@ static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
-EAFNOSUPPORT, -EAFNOSUPPORT,
-EPFNOSUPPORT, -EPFNOSUPPORT,
-EPROTONOSUPPORT, -EPROTONOSUPPORT,
-ESOCKTNOSUPPORT); -ESOCKTNOSUPPORT,
-ENOPROTOOPT);
} }
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED); _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 */ /* Two different errors for access problems */
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) { static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
return IN_SET(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; char *ans, *t, *prev, *prev2;
const char *f; const char *f;
assert(s);
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be /* 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 * 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. * 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 < ' ' || if ((unsigned char) *f < ' ' ||
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) || (!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) if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
break; 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) { char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) {
if (FLAGS_SET(flags, XESCAPE_8_BIT)) if (FLAGS_SET(flags, XESCAPE_8_BIT))
return xescape_full(str, "", console_width, flags); return xescape_full(str, /* bad= */ NULL, console_width, flags);
else else
return utf8_escape_non_printable_full(str, return utf8_escape_non_printable_full(str,
console_width, console_width,
@@ -454,6 +456,12 @@ char* octescape(const char *s, size_t len) {
assert(s || len == 0); 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); t = buf = new(char, len * 4 + 1);
if (!buf) if (!buf)
return NULL; return NULL;

View File

@@ -169,7 +169,10 @@ int fd_nonblock(int fd, bool nonblock) {
if (nflags == flags) if (nflags == flags)
return 0; 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) { int stdio_disable_nonblock(void) {
@@ -212,9 +215,6 @@ int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
continue; continue;
RET_GATHER(r, fd_cloexec(*fd, cloexec)); RET_GATHER(r, fd_cloexec(*fd, cloexec));
if (r >= 0)
r = 1; /* report if we did anything */
} }
return r; return r;
@@ -514,6 +514,16 @@ int pack_fds(int fds[], size_t n_fds) {
return 0; 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) { int same_fd(int a, int b) {
struct stat sta, stb; struct stat sta, stb;
pid_t pid; pid_t pid;
@@ -523,25 +533,57 @@ int same_fd(int a, int b) {
assert(b >= 0); assert(b >= 0);
/* Compares two file descriptors. Note that semantics are quite different depending on whether we /* 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 * have F_DUPFD_QUERY/kcmp() or we don't. If we have F_DUPFD_QUERY/kcmp() this will only return true
* descriptors, but not otherwise. If we don't have kcmp() this will also return true for two fds of * for dup()ed file descriptors, but not otherwise. If we don't have F_DUPFD_QUERY/kcmp() this will
* the same file, created by separate open() calls. Since we use this call mostly for filtering out * also return true for two fds of the same file, created by separate open() calls. Since we use this
* duplicates in the fd store this difference hopefully doesn't matter too much. */ * 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; 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. */ /* Try to use kcmp() if we have it. */
pid = getpid_cached(); pid = getpid_cached();
r = kcmp(pid, pid, KCMP_FILE, a, b); r = kcmp(pid, pid, KCMP_FILE, a, b);
if (r == 0) if (r >= 0)
return true; return !r;
if (r > 0)
return false;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -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) if (fstat(a, &sta) < 0)
return -errno; return -errno;
@@ -572,14 +614,21 @@ int same_fd(int a, int b) {
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
void cmsg_close_all(struct msghdr *mh) { void cmsg_close_all(struct msghdr *mh) {
struct cmsghdr *cmsg;
assert(mh); assert(mh);
CMSG_FOREACH(cmsg, mh) struct cmsghdr *cmsg;
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) CMSG_FOREACH(cmsg, mh) {
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
if (cmsg->cmsg_type == SCM_RIGHTS)
close_many(CMSG_TYPED_DATA(cmsg, int), close_many(CMSG_TYPED_DATA(cmsg, int),
(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(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) { bool fdname_is_valid(const char *s) {
@@ -609,7 +658,6 @@ bool fdname_is_valid(const char *s) {
return p - s <= FDNAME_MAX; return p - s <= FDNAME_MAX;
} }
#if 0 /* NM_IGNORED */
int fd_get_path(int fd, char **ret) { int fd_get_path(int fd, char **ret) {
int r; int r;
@@ -619,19 +667,12 @@ int fd_get_path(int fd, char **ret) {
return safe_getcwd(ret); return safe_getcwd(ret);
r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret); r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
if (r == -ENOENT) { if (r == -ENOENT)
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make return proc_fd_enoent_errno();
* 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 r; return r;
} }
#if 0 /* NM_IGNORED */
int move_fd(int from, int to, int cloexec) { int move_fd(int from, int to, int cloexec) {
int r; 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 */ /* 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) if (fd[i] < 0)
fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */
else if (fd[i] != i && fd[i] < 3) { 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]; 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 /* 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 * 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. */ * 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) { if (fd[i] == i) {
/* fd is already in place, but let's make sure O_CLOEXEC is off */ /* fd is already in place, but let's make sure O_CLOEXEC is off */
r = fd_cloexec(i, false); r = fd_cloexec(i, false);
if (r < 0) if (r < 0)
goto finish; goto finish;
} else { } else {
assert(fd[i] > 2); assert(fd[i] > 2);
@@ -804,7 +840,6 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
goto finish; goto finish;
} }
} }
}
r = 0; r = 0;
@@ -828,8 +863,6 @@ finish:
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
int fd_reopen(int fd, int flags) { int fd_reopen(int fd, int flags) {
int r;
assert(fd >= 0 || fd == AT_FDCWD); assert(fd >= 0 || fd == AT_FDCWD);
assert(!FLAGS_SET(flags, O_CREAT)); assert(!FLAGS_SET(flags, O_CREAT));
@@ -865,13 +898,47 @@ int fd_reopen(int fd, int flags) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
r = proc_mounted(); return proc_fd_enoent_errno();
if (r == 0) }
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is return new_fd;
* not valid, otherwise let's propagate the original }
* error */
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; return new_fd;
@@ -922,21 +989,21 @@ int fd_is_opath(int fd) {
return FLAGS_SET(r, O_PATH); 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; int flags, unexpected_flags;
/* Check if an extrinsic fd is safe to work on (by a privileged service). This ensures that clients /* 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 * 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). * 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, * immediately after open(). It should have no effect whatsoever to an already-opened FD,
* and since we refuse O_PATH it should be safe. * 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. * 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. * 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); assert(fd >= 0);
@@ -945,13 +1012,13 @@ int fd_verify_safe_flags(int fd) {
if (flags < 0) if (flags < 0)
return -errno; 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) if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO), return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o", "Unexpected flags set for extrinsic fd: 0%o",
(unsigned) unexpected_flags); (unsigned) unexpected_flags);
return 0; return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
} }
int read_nr_open(void) { int read_nr_open(void) {
@@ -1055,8 +1122,6 @@ int fds_are_same_mount(int fd1, int fd2) {
int mntid; int mntid;
r = path_get_mnt_id_at_fallback(fd1, "", &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) if (r < 0)
return r; return r;
assert(mntid >= 0); assert(mntid >= 0);
@@ -1069,8 +1134,6 @@ int fds_are_same_mount(int fd1, int fd2) {
int mntid; int mntid;
r = path_get_mnt_id_at_fallback(fd2, "", &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) if (r < 0)
return r; return r;
assert(mntid >= 0); assert(mntid >= 0);
@@ -1103,3 +1166,20 @@ char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid,
return buf; return buf;
} }
#endif /* NM_IGNORED */ #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 pack_fds(int fds[], size_t n);
int fd_validate(int fd);
int same_fd(int a, int b); int same_fd(int a, int b);
void cmsg_close_all(struct msghdr *mh); 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(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_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
int fd_is_opath(int 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 read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret); int fd_get_diskseq(int fd, uint64_t *ret);
@@ -156,6 +162,8 @@ char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid,
#define FORMAT_PROC_PID_FD_PATH(pid, fd) \ #define FORMAT_PROC_PID_FD_PATH(pid, fd) \
format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd)) format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd))
int proc_fd_enoent_errno(void);
const char* accmode_to_string(int flags); const char* accmode_to_string(int flags);
/* Like ASSERT_PTR, but for fds */ /* Like ASSERT_PTR, but for fds */

View File

@@ -16,10 +16,12 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "chase.h" #include "chase.h"
#include "extract-word.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "hexdecoct.h" #include "hexdecoct.h"
#include "label.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "mkdir.h" #include "mkdir.h"
@@ -34,7 +36,7 @@
#include "tmpfile-util.h" #include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */ /* 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 */ /* 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 #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/ * 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 * allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we
* can detect EOFs. */ * 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) { int fdopen_unlocked(int fd, const char *options, FILE **ret) {
assert(ret); assert(ret);
@@ -121,7 +123,7 @@ FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode) {
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int write_string_stream_ts( int write_string_stream_full(
FILE *f, FILE *f,
const char *line, const char *line,
WriteStringFileFlags flags, 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 /* 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. */ * 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) if (r < 0)
return r; 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) if (r < 0)
goto fail; goto fail;
r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags)); if (call_label_ops_post) {
if (r < 0) call_label_ops_post = false;
goto fail;
if (renameat(dir_fd, p, dir_fd, fn) < 0) { r = label_ops_post(fileno(f), /* path= */ NULL, /* created= */ true);
r = -errno; if (r < 0)
goto fail; 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)) { if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) {
/* Sync the rename, too */ /* Sync the rename, too */
r = fsync_directory_of_file(fileno(f)); r = fsync_directory_of_file(fileno(f));
@@ -260,20 +280,26 @@ static int write_string_file_atomic_at(
return 0; return 0;
fail: 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); (void) unlinkat(dir_fd, p, 0);
return r; return r;
} }
int write_string_file_ts_at( int write_string_file_full(
int dir_fd, int dir_fd,
const char *fn, const char *fn,
const char *line, const char *line,
WriteStringFileFlags flags, 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_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
int q, r; int r;
assert(fn); assert(fn);
assert(line); assert(line);
@@ -295,18 +321,37 @@ int write_string_file_ts_at(
goto fail; goto fail;
return r; 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. */ /* 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_NOFOLLOW) ? O_NOFOLLOW : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY), (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
write_string_file_flags_to_mode(flags)); mode,
&made_file);
if (fd < 0) { 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; goto fail;
} }
@@ -317,26 +362,31 @@ int write_string_file_ts_at(
if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
setvbuf(f, NULL, _IONBF, 0); 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) if (r < 0)
goto fail; goto fail;
return 0; return 0;
fail: 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)) if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
return r; return r;
f = safe_fclose(f); f = safe_fclose(f);
fd = safe_close(fd);
/* OK, the operation failed, but let's see if the right /* OK, the operation failed, but let's see if the right contents in place already. If so, eat up the
* contents in place already. If so, eat up the error. */ * error. */
if (verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE));
if (q <= 0)
return r;
return 0; return 0;
return r;
} }
int write_string_filef( int write_string_filef(
@@ -358,6 +408,22 @@ int write_string_filef(
return write_string_file(fn, p, flags); 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) { int read_one_line_file_at(int dir_fd, const char *filename, char **ret) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
int r; int r;
@@ -796,35 +862,49 @@ int read_full_file_full(
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int executable_is_script(const char *path, char **interpreter) { int script_get_shebang_interpreter(const char *path, char **ret) {
_cleanup_free_ char *line = NULL; _cleanup_fclose_ FILE *f = NULL;
size_t len;
char *ans;
int r; int r;
assert(path); assert(path);
r = read_one_line_file(path, &line); f = fopen(path, "re");
if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ if (!f)
return 0; 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) if (r < 0)
return r; 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; 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; return 0;
} }
DIR *xopendirat(int fd, const char *name, int flags) { DIR* xopendirat(int dir_fd, const char *name, int flags) {
_cleanup_close_ int nfd = -EBADF; _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); return opendir(name);
nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); fd = openat(dir_fd, name, O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags);
if (nfd < 0) if (fd < 0)
return NULL; return NULL;
return take_fdopendir(&nfd); return take_fdopendir(&fd);
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
@@ -1364,6 +1446,29 @@ int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *sp
return 0; 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 */ #if 0 /* NM_IGNORED */
/* A bitmask of the EOL markers we know */ /* A bitmask of the EOL markers we know */
typedef enum EndOfLineMarker { 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) { int read_stripped_line(FILE *f, size_t limit, char **ret) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
int r; int r, k;
assert(f); assert(f);
@@ -1524,23 +1629,17 @@ int read_stripped_line(FILE *f, size_t limit, char **ret) {
return r; return r;
if (ret) { if (ret) {
const char *p; const char *p = strstrip(s);
p = strstrip(s);
if (p == s) if (p == s)
*ret = TAKE_PTR(s); *ret = TAKE_PTR(s);
else { else {
char *copy; k = strdup_to(ret, p);
if (k < 0)
copy = strdup(p); return k;
if (!copy)
return -ENOMEM;
*ret = copy;
} }
} }
return r; return r > 0; /* Return 1 if something was read. */
} }
int safe_fgetc(FILE *f, char *ret) { 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_0600 = 1 << 10,
WRITE_STRING_FILE_MODE_0444 = 1 << 11, WRITE_STRING_FILE_MODE_0444 = 1 << 11,
WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
WRITE_STRING_FILE_LABEL = 1 << 13,
/* 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. */
} WriteStringFileFlags; } WriteStringFileFlags;
typedef enum { typedef enum {
@@ -51,23 +47,22 @@ DIR* take_fdopendir(int *dfd);
FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc); FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc);
FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode); 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) { static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
return write_string_stream_ts(f, line, flags, NULL); return write_string_stream_full(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);
} }
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_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); 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) { static inline int read_one_line_file(const char *filename, char **ret) {
return read_one_line_file_at(AT_FDCWD, filename, 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); 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); 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 { typedef enum XfopenFlags {
XFOPEN_UNLOCKED = 1 << 0, /* call __fsetlocking(FSETLOCKING_BYCALLER) after opened */ 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 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_separator(FILE *f, const char *s, const char *separator, bool *space);
int fputs_with_newline(FILE *f, const char *s);
typedef enum ReadLineFlags { typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0, READ_LINE_ONLY_NUL = 1 << 0,
@@ -152,6 +148,14 @@ typedef enum ReadLineFlags {
} ReadLineFlags; } ReadLineFlags;
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); 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) { static inline bool file_offset_beyond_memory_size(off_t x) {
if (x < 0) /* off_t is signed, filter that out */ 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; 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 safe_fgetc(FILE *f, char *ret);
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); 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,43 +7,6 @@
#include "stdio-util.h" #include "stdio-util.h"
#include "strxcpyx.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 { typedef struct {
const char *suffix; const char *suffix;
@@ -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++) for (size_t i = 0; i < n; i++)
if (t >= table[i].factor) { 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, (void) snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s", "%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor, t / table[i].factor,
i != n - 1 ? remainder,
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
table[i].suffix); table[i].suffix);
} else else
(void) snprintf(buf, l, (void) snprintf(buf, l,
"%" PRIu64 "%s", "%" PRIu64 "%s",
t / table[i].factor, t / table[i].factor,

View File

@@ -2,7 +2,6 @@
#pragma once #pragma once
#include <inttypes.h> #include <inttypes.h>
#include <net/if.h>
#include <stdbool.h> #include <stdbool.h>
#include "cgroup-util.h" #include "cgroup-util.h"
@@ -68,29 +67,6 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t));
# error Unknown ino_t size # error Unknown ino_t size
#endif #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 { typedef enum {
FORMAT_BYTES_USE_IEC = 1 << 0, FORMAT_BYTES_USE_IEC = 1 << 0,
FORMAT_BYTES_BELOW_POINT = 1 << 1, FORMAT_BYTES_BELOW_POINT = 1 << 1,

View File

@@ -12,6 +12,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "btrfs.h" #include "btrfs.h"
#include "chattr-util.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.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) { int readlink_value(const char *p, char **ret) {
_cleanup_free_ char *link = NULL, *name = NULL; _cleanup_free_ char *link = NULL, *name = NULL;
int r; int r;
@@ -318,10 +315,7 @@ int fchmod_opath(int fd, mode_t m) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
if (proc_mounted() == 0) return proc_fd_enoent_errno();
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return -ENOENT;
} }
return 0; return 0;
@@ -330,14 +324,21 @@ int fchmod_opath(int fd, mode_t m) {
int futimens_opath(int fd, const struct timespec ts[2]) { int futimens_opath(int fd, const struct timespec ts[2]) {
/* Similar to fchmod_opath() but for futimens() */ /* 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) if (errno != ENOENT)
return -errno; return -errno;
if (proc_mounted() == 0) return proc_fd_enoent_errno();
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return -ENOENT;
} }
return 0; return 0;
@@ -375,9 +376,21 @@ int fd_warn_permissions(const char *path, int fd) {
return stat_warn_permissions(path, &st); 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) { 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; _cleanup_close_ int fd = -EBADF;
int r, ret; int ret;
assert(path); 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. */ * something fchown(), fchmod(), futimensat() don't allow. */
ret = fchmod_and_chown(fd, mode, uid, gid); ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) { return RET_GATHER(ret, touch_fd(fd, stamp));
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;
} }
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; _cleanup_free_ char *relpath = NULL;
int r; int r;
@@ -438,13 +440,13 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) {
from = relpath; from = relpath;
} }
if (symlink(from, to) < 0) { if (symlinkat(from, atfd, to) < 0) {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
if (errno != EEXIST) if (errno != EEXIST)
return -errno; 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 */ if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
return -EEXIST; return -EEXIST;
if (r < 0) /* Any other error? In that case propagate it as is */ 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; return 0;
} }
k = is_dir(def, true); k = is_dir(def, /* follow = */ true);
if (k == 0) if (k == 0)
k = -ENOTDIR; k = -ENOTDIR;
if (k < 0) if (k < 0)
return r < 0 ? r : k; return RET_GATHER(r, k);
*ret = def; *ret = def;
return 0; return 0;
@@ -644,6 +646,7 @@ static int tmp_dir_internal(const char *def, const char **ret) {
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int var_tmp_dir(const char **ret) { 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 /* 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 * 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 */ #endif /* NM_IGNORED */
int tmp_dir(const char **ret) { 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 /* 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. */ * backed by an in-memory file system: /tmp. */
@@ -664,6 +668,8 @@ int tmp_dir(const char **ret) {
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int unlink_or_warn(const char *filename) { int unlink_or_warn(const char *filename) {
assert(filename);
if (unlink(filename) < 0 && errno != ENOENT) if (unlink(filename) < 0 && errno != ENOENT)
/* If the file doesn't exist and the fs simply was read-only (in which /* 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 * 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) { int access_fd(int fd, int mode) {
assert(fd >= 0);
/* Like access() but operates on an already open fd */ /* 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 (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's return proc_fd_enoent_errno();
* 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 0; 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) { int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
_cleanup_close_ int truncate_fd = -EBADF; _cleanup_close_ int truncate_fd = -EBADF;
struct stat st; struct stat st;
off_t l, bs; off_t l, bs;
assert(fd >= 0 || fd == AT_FDCWD);
assert(name);
assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0); 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 /* 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; 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_close_ int fd = -EBADF, parent_fd = -EBADF;
_cleanup_free_ char *fname = NULL, *parent = NULL; _cleanup_free_ char *fname = NULL, *parent = NULL;
int r; int r;
@@ -1064,7 +1066,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
path = fname; 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)) if (IN_SET(fd, -ELOOP, -ENOTDIR))
return -EEXIST; return -EEXIST;
if (fd < 0) 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) { int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) {
unsigned attempts = 7;
int fd; int fd;
/* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if /* 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 * it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will
* shortcut to openat() otherwise */ * shortcut to openat() otherwise.
*
if (!ret_newly_created) * Note that this routine is a bit more strict with symlinks than regular openat() is. If O_NOFOLLOW
return RET_NERRNO(openat(dirfd, pathname, flags, mode)); * 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)) { if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) {
fd = openat(dirfd, pathname, flags, mode); fd = openat(dirfd, pathname, flags, mode);
if (fd < 0) if (fd < 0)
return -errno; return -errno;
if (ret_newly_created)
*ret_newly_created = FLAGS_SET(flags, O_CREAT); *ret_newly_created = FLAGS_SET(flags, O_CREAT);
return fd; return fd;
} }
for (;;) { for (unsigned attempts = 7;;) {
/* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */ /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode); fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
if (fd >= 0) { if (fd >= 0) {
if (ret_newly_created)
*ret_newly_created = false; *ret_newly_created = false;
return fd; return fd;
} }
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
/* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */ /* 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, mode); fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
if (fd >= 0) { if (fd >= 0) {
if (ret_newly_created)
*ret_newly_created = true; *ret_newly_created = true;
return fd; return fd;
} }
if (errno != EEXIST) if (errno != EEXIST)
return -errno; return -errno;
/* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again /* Hmm, so now we got EEXIST? Then someone might have created the file between the first and
* without the two flags. But let's not spin forever, hence put a limit on things */ * 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 */ if (--attempts == 0) /* Give up eventually, somebody is playing with us */
return -EEXIST; 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) { int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
bool made = false; bool made_dir = false, made_file = false;
int r; int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD); 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: /* 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 * • 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 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 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)) { if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL)); 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); 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)) { 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); r = label_ops_pre(dir_fd, path, FLAGS_SET(open_flags, O_DIRECTORY) ? S_IFDIR : S_IFREG);
if (r < 0) if (r < 0)
return r; return r;
call_label_ops_post = true;
} }
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) { 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 (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL)) if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST; return -EEXIST;
made = false;
} else if (r < 0) } else if (r < 0)
return r; return r;
else else
made = true; made_dir = true;
if (FLAGS_SET(xopen_flags, XO_LABEL)) {
r = label_ops_post(dir_fd, path);
if (r < 0)
return r;
}
open_flags &= ~(O_EXCL|O_CREAT); 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 (fd < 0) {
if (IN_SET(fd, r = -errno;
/* We got ENOENT? then someone else immediately removed it after we goto error;
* 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;
} }
if (FLAGS_SET(open_flags, O_CREAT) && FLAGS_SET(xopen_flags, XO_LABEL)) { r = fd_verify_regular(fd);
r = label_ops_post(dir_fd, path);
if (r < 0) 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); 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 */ #if 0 /* NM_IGNORED */
@@ -1247,4 +1327,103 @@ int xopenat_lock_full(
return TAKE_FD(fd); 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 */ #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 rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
int readlinkat_malloc(int fd, const char *p, char **ret); 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_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); 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) { 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 fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st); 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)) 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); 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) { static inline int touch(const char *path) {
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); 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); 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) { 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); int access_fd(int fd, int mode);
void unlink_tempfilep(char (*p)[]);
typedef enum UnlinkDeallocateFlags { typedef enum UnlinkDeallocateFlags {
UNLINK_REMOVEDIR = 1 << 0, UNLINK_REMOVEDIR = 1 << 0,
UNLINK_ERASE = 1 << 1, 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 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); 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); 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) { static inline int xopenat(int dir_fd, const char *path, int open_flags) {
return xopenat_full(dir_fd, path, open_flags, 0, 0); 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) { 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); 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

@@ -82,6 +82,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o", [SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o", [SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o", [SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S",
[SPECIAL_GLYPH_IDCARD] = "@",
}, },
/* UTF-8 */ /* UTF-8 */
@@ -151,6 +153,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡", [SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵", [SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢", [SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
[SPECIAL_GLYPH_IDCARD] = u8"🪪",
}, },
}; };

View File

@@ -55,6 +55,8 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_YELLOW_CIRCLE, SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE, SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE, SPECIAL_GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_SUPERHERO,
SPECIAL_GLYPH_IDCARD,
_SPECIAL_GLYPH_MAX, _SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL, _SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph; } SpecialGlyph;

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); 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) { static void hashmap_free_no_clear(HashmapBase *h) {
assert(!h->has_indirect); assert(!h->has_indirect);
assert(h->n_direct_entries == 0); 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_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 _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 _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_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 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 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); 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) #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); IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h);
static inline IteratedCache* hashmap_iterated_cache_new(Hashmap *h) { static inline IteratedCache* hashmap_iterated_cache_new(Hashmap *h) {
return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));

View File

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

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 */ #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 in_addr_prefix_intersect(
int family, int family,
const union in_addr_union *a, const union in_addr_union *a,
@@ -204,51 +250,16 @@ int in_addr_prefix_intersect(
const union in_addr_union *b, const union in_addr_union *b,
unsigned bprefixlen) { unsigned bprefixlen) {
unsigned m;
assert(a); assert(a);
assert(b); 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) { if (family == AF_INET6)
uint32_t x, nm; return in6_addr_prefix_intersect(&a->in6, aprefixlen, &b->in6, bprefixlen);
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;
}
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
} }
@@ -888,7 +899,7 @@ int in_addr_prefix_from_string(
return 0; return 0;
} }
int in_addr_prefix_from_string_auto_internal( int in_addr_prefix_from_string_auto_full(
const char *p, const char *p,
InAddrPrefixLenMode mode, InAddrPrefixLenMode mode,
int *ret_family, 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 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); 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_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_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_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); 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. */ PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */
} InAddrPrefixLenMode; } 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) { 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) { 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 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 */ /* 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); 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) if (errno != ENOENT)
return -errno; return -errno;
/* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */ return proc_fd_enoent_errno();
r = proc_mounted();
if (r == 0)
return -ENOSYS;
if (r > 0)
return -EBADF;
return -ENOENT; /* OK, no clue, let's propagate the original error */
} }
return wd; return wd;

View File

@@ -6,25 +6,16 @@
#include <sys/uio.h> #include <sys/uio.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "iovec-util-fundamental.h"
#include "macro.h" #include "macro.h"
/* An iovec pointing to a single NUL byte */ extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */
#define IOVEC_NUL_BYTE (const struct iovec) { \ extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. non-NULL) pointer */
.iov_base = (void*) (const uint8_t[1]) { 0 }, \
.iov_len = 1, \
}
size_t iovec_total_size(const struct iovec *iovec, size_t n); size_t iovec_total_size(const struct iovec *iovec, size_t n);
bool iovec_increment(struct iovec *iovec, size_t n, size_t k); 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) { static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
assert(iovec); assert(iovec);
/* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */ /* 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), \ .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) { static inline void iovec_done_erase(struct iovec *iovec) {
assert(iovec); assert(iovec);
@@ -56,16 +39,6 @@ static inline void iovec_done_erase(struct iovec *iovec) {
iovec->iov_len = 0; 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(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); 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; return ret;
} }
struct iovec* iovec_append(struct iovec *iovec, const struct iovec *append);

View File

@@ -6,10 +6,13 @@
#include <stddef.h> #include <stddef.h>
#include "label.h" #include "label.h"
#include "macro.h"
static const LabelOps *label_ops = NULL; static const LabelOps *label_ops = NULL;
int label_ops_set(const LabelOps *ops) { int label_ops_set(const LabelOps *ops) {
assert(ops);
if (label_ops) if (label_ops)
return -EBUSY; return -EBUSY;
@@ -17,6 +20,10 @@ int label_ops_set(const LabelOps *ops) {
return 0; return 0;
} }
void label_ops_reset(void) {
label_ops = NULL;
}
int label_ops_pre(int dir_fd, const char *path, mode_t mode) { int label_ops_pre(int dir_fd, const char *path, mode_t mode) {
if (!label_ops || !label_ops->pre) if (!label_ops || !label_ops->pre)
return 0; 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); 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) if (!label_ops || !label_ops->post)
return 0; 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 */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include <stdbool.h>
#include <sys/types.h> #include <sys/types.h>
typedef struct LabelOps { typedef struct LabelOps {
int (*pre)(int dir_fd, const char *path, mode_t mode); 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; } LabelOps;
int label_ops_set(const LabelOps *label_ops); 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_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 */ if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */
return true; return true;
_cleanup_(freelocalep) locale_t loc = _cleanup_(freelocalep) locale_t loc = newlocale(LC_ALL_MASK, name, (locale_t) 0);
newlocale(LC_ALL_MASK, name, 0);
if (loc == (locale_t) 0) if (loc == (locale_t) 0)
return errno == ENOMEM ? -ENOMEM : false; return errno == ENOMEM ? -ENOMEM : false;

View File

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

View File

@@ -18,25 +18,26 @@ struct signalfd_siginfo;
typedef enum LogTarget{ typedef enum LogTarget{
LOG_TARGET_CONSOLE, LOG_TARGET_CONSOLE,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_KMSG, LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL, LOG_TARGET_JOURNAL,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG, LOG_TARGET_SYSLOG,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */ LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_NULL, 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, _LOG_TARGET_INVALID = -EINVAL,
} LogTarget; } LogTarget;
/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be /* 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) #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) (abs(num) | (1 << 30))
#define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) #define IS_SYNTHETIC_ERRNO(val) (((val) >> 30) == 1)
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & ~(1 << 30)) #define ERRNO_VALUE(val) (abs(val) & ~(1 << 30))
/* The callback function to be invoked when syntax warnings are seen /* The callback function to be invoked when syntax warnings are seen
@@ -56,7 +57,7 @@ int log_set_target_from_string(const char *e);
LogTarget log_get_target(void) _pure_; LogTarget log_get_target(void) _pure_;
void log_settle_target(void); 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); int log_set_max_level_from_string(const char *e);
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int log_get_max_level(void) _pure_; int log_get_max_level(void) _pure_;
@@ -67,6 +68,7 @@ log_get_max_level(void)
return 7 /* LOG_DEBUG */; return 7 /* LOG_DEBUG */;
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
int log_max_levels_to_string(int level, char **ret);
void log_set_facility(int facility); void log_set_facility(int facility);
@@ -95,6 +97,7 @@ assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE __FILE__ #define PROJECT_FILE __FILE__
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
bool stderr_is_journal(void);
int log_open(void); int log_open(void);
void log_close(void); void log_close(void);
void log_forget_fds(void); void log_forget_fds(void);
@@ -434,9 +437,10 @@ int log_emergency_level(void);
#define log_dump(level, buffer) \ #define log_dump(level, buffer) \
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, 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_full(level) log_oom_internal(level, PROJECT_FILE, __LINE__, __func__)
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__) #define log_oom() log_oom_full(LOG_ERR)
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__) #define log_oom_debug() log_oom_full(LOG_DEBUG)
#define log_oom_warning() log_oom_full(LOG_WARNING)
bool log_on_console(void) _pure_; bool log_on_console(void) _pure_;
@@ -498,6 +502,18 @@ int log_syntax_invalid_utf8_internal(
const char *func, const char *func,
const char *rvalue); 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, ...) \ #define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \ ({ \
int _level = (level), _e = (error); \ int _level = (level), _e = (error); \
@@ -514,6 +530,12 @@ int log_syntax_invalid_utf8_internal(
: -EINVAL; \ : -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) #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
void log_setup(void); void log_setup(void);
@@ -630,6 +652,15 @@ size_t log_context_num_contexts(void);
/* Returns the number of fields in all attached log contexts. */ /* Returns the number of fields in all attached log contexts. */
size_t log_context_num_fields(void); 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(...) \ #define LOG_CONTEXT_PUSH(...) \
LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__)) LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))

View File

@@ -30,60 +30,6 @@
#define _function_no_sanitize_float_cast_overflow_ #define _function_no_sanitize_float_cast_overflow_
#endif #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 */ /* test harness */
#define EXIT_TEST_SKIP 77 #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 PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) #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_TO_STR(x) ((char[2]) { x, 0 })
#define char_array_0(x) x[sizeof(x)-1] = 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' /* Maximum buffer size needed for formatting an unsigned integer type as hex, including space for '0x'
* prefix and trailing NUL suffix. */ * prefix and trailing NUL suffix. */
#define HEXADECIMAL_STR_MAX(type) (2 + sizeof(type) * 2 + 1) #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 */ /* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_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) \ #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \ scope type *name##_ref(type *p) { \
if (!p) \ if (!p) \

View File

@@ -35,6 +35,13 @@ static inline void *mempcpy_safe(void *dst, const void *src, size_t n) {
return mempcpy(dst, src, n); 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. */ /* 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) { static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
if (n == 0) if (n == 0)

View File

@@ -7,6 +7,10 @@
#define F_LINUX_SPECIFIC_BASE 1024 #define F_LINUX_SPECIFIC_BASE 1024
#endif #endif
#ifndef F_DUPFD_QUERY
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
#endif
#ifndef F_SETPIPE_SZ #ifndef F_SETPIPE_SZ
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) #define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
#endif #endif
@@ -92,3 +96,7 @@
#define RAW_O_LARGEFILE 00100000 #define RAW_O_LARGEFILE 00100000
#endif #endif
#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,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#if USE_SYS_RANDOM_H #include "macro.h"
#if HAVE_GETRANDOM
# include <sys/random.h> # include <sys/random.h>
#else #else
# include <linux/random.h> # include <linux/random.h>
@@ -9,12 +11,18 @@
#ifndef GRND_NONBLOCK #ifndef GRND_NONBLOCK
# define GRND_NONBLOCK 0x0001 # define GRND_NONBLOCK 0x0001
#else
assert_cc(GRND_NONBLOCK == 0x0001);
#endif #endif
#ifndef GRND_RANDOM #ifndef GRND_RANDOM
# define GRND_RANDOM 0x0002 # define GRND_RANDOM 0x0002
#else
assert_cc(GRND_RANDOM == 0x0002);
#endif #endif
#ifndef GRND_INSECURE #ifndef GRND_INSECURE
# define GRND_INSECURE 0x0004 # define GRND_INSECURE 0x0004
#else
assert_cc(GRND_INSECURE == 0x0004);
#endif #endif

View File

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

View File

@@ -22,6 +22,7 @@
#include "macro.h" #include "macro.h"
#include "missing_keyctl.h" #include "missing_keyctl.h"
#include "missing_sched.h"
#include "missing_stat.h" #include "missing_stat.h"
#include "missing_syscall_def.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 #if !HAVE_MEMFD_CREATE
static inline int missing_memfd_create(const char *name, unsigned int flags) { static inline int missing_memfd_create(const char *name, unsigned int flags) {
# ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags); return syscall(__NR_memfd_create, name, flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define memfd_create missing_memfd_create # 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 #if !HAVE_GETRANDOM
/* glibc says getrandom() returns ssize_t */ /* glibc says getrandom() returns ssize_t */
static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) { static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
# ifdef __NR_getrandom
return syscall(__NR_getrandom, buffer, count, flags); return syscall(__NR_getrandom, buffer, count, flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define getrandom missing_getrandom # 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 #if !HAVE_SETNS
static inline int missing_setns(int fd, int nstype) { static inline int missing_setns(int fd, int nstype) {
# ifdef __NR_setns
return syscall(__NR_setns, fd, nstype); return syscall(__NR_setns, fd, nstype);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define setns missing_setns # define setns missing_setns
@@ -175,12 +161,7 @@ static inline pid_t raw_getpid(void) {
#if !HAVE_RENAMEAT2 #if !HAVE_RENAMEAT2
static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { 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); return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define renameat2 missing_renameat2 # define renameat2 missing_renameat2
@@ -190,12 +171,7 @@ static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, c
#if !HAVE_KCMP #if !HAVE_KCMP
static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { 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); return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define kcmp missing_kcmp # 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 #if !HAVE_KEYCTL
static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { 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); return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
# else
errno = ENOSYS;
return -1;
# endif
# define keyctl missing_keyctl # 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) { 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); return syscall(__NR_add_key, type, description, payload, plen, ringid);
# else
errno = ENOSYS;
return -1;
# endif
# define add_key missing_add_key # 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) { 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); return syscall(__NR_request_key, type, description, callout_info, destringid);
# else
errno = ENOSYS;
return -1;
# endif
# define request_key missing_request_key # 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 #if !HAVE_PIDFD_SEND_SIGNAL
static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { 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); return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define pidfd_send_signal missing_pidfd_send_signal # 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 #if !HAVE_PIDFD_OPEN
static inline int missing_pidfd_open(pid_t pid, unsigned flags) { static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
# ifdef __NR_pidfd_open
return syscall(__NR_pidfd_open, pid, flags); return syscall(__NR_pidfd_open, pid, flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define pidfd_open missing_pidfd_open # 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 # define getdents64 missing_getdents64
#endif #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 */ #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. */ * at build time) and just define it. Once the kernel drops ia64 support, we can drop this too. */
#define HAVE_CLONE 1 #define HAVE_CLONE 1
#endif #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 #if HAVE_THREADS_H
# include <threads.h> # include <threads.h>
#elif !(defined(thread_local)) #elif !(defined(thread_local))
/* Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ # ifndef __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))
# define thread_local _Thread_local # define thread_local _Thread_local
# else # else
# define thread_local __thread # define thread_local __thread

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 <sys/types.h>
#include "pidref.h"
typedef enum NamespaceType { typedef enum NamespaceType {
NAMESPACE_CGROUP, NAMESPACE_CGROUP,
NAMESPACE_IPC, NAMESPACE_IPC,
@@ -19,9 +21,23 @@ typedef enum NamespaceType {
extern const struct namespace_info { extern const struct namespace_info {
const char *proc_name; const char *proc_name;
const char *proc_path; 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]; } 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( int namespace_open(
pid_t pid, pid_t pid,
int *ret_pidns_fd, int *ret_pidns_fd,
@@ -29,11 +45,28 @@ int namespace_open(
int *ret_netns_fd, int *ret_netns_fd,
int *ret_userns_fd, int *ret_userns_fd,
int *ret_root_fd); int *ret_root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int 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(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) { 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 /* 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; 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 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 <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <linux/ipv6.h>
#include <net/if.h> #include <net/if.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -721,22 +722,6 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow
return 0; 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 parse_oom_score_adjust(const char *s, int *ret) {
int r, v; 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(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_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); int parse_oom_score_adjust(const char *s, int *ret);
/* Implement floating point using fixed integers, to improve performance when /* 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); return path_join(prefix, p);
} }
#endif /* NM_IGNORED */
int safe_getcwd(char **ret) { int safe_getcwd(char **ret) {
_cleanup_free_ char *cwd = NULL; _cleanup_free_ char *cwd = NULL;
@@ -73,6 +74,7 @@ int safe_getcwd(char **ret) {
return 0; return 0;
} }
#if 0 /* NM_IGNORED */
int path_make_absolute_cwd(const char *p, char **ret) { int path_make_absolute_cwd(const char *p, char **ret) {
char *c; char *c;
int r; int r;
@@ -221,8 +223,10 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
char* path_startswith_strv(const char *p, char **set) { char* path_startswith_strv(const char *p, char * const *strv) {
STRV_FOREACH(s, set) { assert(p);
STRV_FOREACH(s, strv) {
char *t; char *t;
t = path_startswith(p, *s); t = path_startswith(p, *s);
@@ -531,6 +535,20 @@ int path_compare_filename(const char *a, const char *b) {
return strcmp(fa, fb); 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, ...) { char* path_extend_internal(char **x, ...) {
size_t sz, old_sz; size_t sz, old_sz;
char *q, *nx; 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); r = path_make_absolute_cwd(name, ret_filename);
if (r < 0) if (r < 0)
return r; return r;
path_simplify(*ret_filename);
} }
if (ret_fd) 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( int find_executable_full(
const char *name, const char *name,
const char *root, const char *root,
char **exec_search_path, char * const *exec_search_path,
bool use_path_envvar, bool use_path_envvar,
char **ret_filename, char **ret_filename,
int *ret_fd) { int *ret_fd) {
int last_error = -ENOENT, r = 0; int last_error = -ENOENT, r = 0;
const char *p = NULL;
assert(name); assert(name);
if (is_path(name)) if (is_path(name))
return find_executable_impl(name, root, ret_filename, ret_fd); 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) { if (exec_search_path) {
STRV_FOREACH(element, exec_search_path) { STRV_FOREACH(element, exec_search_path) {
_cleanup_free_ char *full_path = NULL; _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; continue;
}
full_path = path_join(*element, name); full_path = path_join(*element, name);
if (!full_path) if (!full_path)
return -ENOMEM; return -ENOMEM;
r = find_executable_impl(full_path, root, ret_filename, ret_fd); r = find_executable_impl(full_path, root, ret_filename, ret_fd);
if (r < 0) { if (r >= 0)
return 0;
if (r != -EACCES) if (r != -EACCES)
last_error = r; last_error = r;
continue;
}
return 0;
} }
return last_error; 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 */ /* Resolve a single-component name to a full path */
for (;;) { for (;;) {
_cleanup_free_ char *element = NULL; _cleanup_free_ char *element = NULL;
@@ -725,22 +746,20 @@ int find_executable_full(
if (r == 0) if (r == 0)
break; break;
if (!path_is_absolute(element)) if (!path_is_absolute(element)) {
log_debug("Exec search path '%s' isn't absolute, ignoring.", element);
continue; continue;
}
if (!path_extend(&element, name)) if (!path_extend(&element, name))
return -ENOMEM; return -ENOMEM;
r = find_executable_impl(element, root, ret_filename, ret_fd); 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. */ /* PATH entries which we don't have access to are ignored, as per tradition. */
if (r != -EACCES) if (r != -EACCES)
last_error = r; last_error = r;
continue;
}
/* Found it! */
return 0;
} }
return last_error; 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) { int path_extract_directory(const char *path, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c, *next = NULL; const char *c, *next = NULL;
int r; int r;
@@ -1126,14 +1144,10 @@ int path_extract_directory(const char *path, char **ret) {
if (*path != '/') /* filename only */ if (*path != '/') /* filename only */
return -EDESTADDRREQ; return -EDESTADDRREQ;
a = strdup("/"); return strdup_to(ret, "/");
if (!a)
return -ENOMEM;
*ret = TAKE_PTR(a);
return 0;
} }
a = strndup(path, next - path); _cleanup_free_ char *a = strndup(path, next - path);
if (!a) if (!a)
return -ENOMEM; return -ENOMEM;
@@ -1347,6 +1361,20 @@ bool dot_or_dot_dot(const char *path) {
} }
#if 0 /* NM_IGNORED */ #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) { bool empty_or_root(const char *path) {
/* For operations relative to some root directory, returns true if the specified root directory is /* 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, "/"); 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) STRV_FOREACH(i, l)
if (path_equal(*i, path)) if (path_equal(*i, path))
return true; return true;
@@ -1366,7 +1396,9 @@ bool path_strv_contains(char **l, const char *path) {
return false; 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) { STRV_FOREACH(i, l) {
const char *j = *i; const char *j = *i;
@@ -1374,6 +1406,7 @@ bool prefixed_path_strv_contains(char **l, const char *path) {
j++; j++;
if (*j == '+') if (*j == '+')
j++; j++;
if (path_equal(j, path)) if (path_equal(j, path))
return true; return true;
} }
@@ -1443,4 +1476,32 @@ int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
*ret = NULL; *ret = NULL;
return false; 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 */ #endif /* NM_IGNORED */

View File

@@ -12,27 +12,26 @@
#include "time-util.h" #include "time-util.h"
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin" #define PATH_SPLIT_BIN(x) x "sbin:" x "bin"
#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0" #define PATH_SPLIT_BIN_NULSTR(x) x "sbin\0" x "bin\0"
#define PATH_NORMAL_SBIN_BIN(x) x "bin" #define PATH_MERGED_BIN(x) x "bin"
#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0" #define PATH_MERGED_BIN_NULSTR(x) x "bin\0"
#if HAVE_SPLIT_BIN #define DEFAULT_PATH_WITH_SBIN PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/")
# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x) #define DEFAULT_PATH_WITHOUT_SBIN PATH_MERGED_BIN("/usr/local/") ":" PATH_MERGED_BIN("/usr/")
# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x)
#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 #else
# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x) return default_PATH();
# 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
#endif #endif
}
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
static inline bool is_path(const char *p) { 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; 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) { 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, ...); char* path_extend_internal(char **x, ...);
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX) #define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
#define path_join(...) path_extend_internal(NULL, __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 { typedef enum PathSimplifyFlags {
PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0, PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
} PathSimplifyFlags; } PathSimplifyFlags;
@@ -103,21 +107,23 @@ static inline int path_simplify_alloc(const char *path, char **ret) {
return 0; 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. */ /* Note: the search terminates on the first NULL item. */
#define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p) #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__)) #define PATH_STARTSWITH_SET(p, ...) path_startswith_strv(p, STRV_MAKE(__VA_ARGS__))
int path_strv_make_absolute_cwd(char **l); int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *root); char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(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) { static inline int find_executable(const char *name, char **ret_filename) {
return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL); return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL);
} }
@@ -203,6 +209,8 @@ bool valid_device_allow_pattern(const char *path);
bool dot_or_dot_dot(const char *path); bool dot_or_dot_dot(const char *path);
bool path_implies_directory(const char *path);
static inline const char* skip_dev_prefix(const char *p) { static inline const char* skip_dev_prefix(const char *p) {
const char *e; const char *e;
@@ -218,7 +226,7 @@ static inline const char* empty_to_root(const char *path) {
return isempty(path) ? "/" : path; return isempty(path) ? "/" : path;
} }
bool path_strv_contains(char **l, const char *path); bool path_strv_contains(char * const *l, const char *path);
bool prefixed_path_strv_contains(char **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); 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 */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
typedef struct PidRef PidRef;
#include "macro.h" #include "macro.h"
#include "process-util.h"
/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */ /* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes
typedef struct PidRef { * continuously. This combines a PID, a modern Linux pidfd and the 64bit inode number of the pidfd into one
pid_t pid; /* always valid */ * structure. Note that depending on kernel support the pidfd might not be initialized, and if it is
int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */ * initialized then fd_id might still not be initialized (because the concept was added to the kernel much
} PidRef; * 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 /* 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) */ * pidref_set_pid() which does so *with* acquiring one, see below) */
@@ -19,17 +54,16 @@ static inline bool pidref_is_set(const PidRef *pidref) {
return pidref && pidref->pid > 0; 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)) { static inline bool pidref_is_remote(const PidRef *pidref) {
if (!pidref_is_set(b)) /* If the fd is set to -EREMOTE we assume PidRef does not refer to a local PID, but on another
return false; * machine (and we just got the PidRef initialized due to deserialization of some RPC message) */
return pidref_is_set(pidref) && pidref->fd == -EREMOTE;
return a->pid == b->pid;
} }
return !pidref_is_set(b); 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 /* 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.) */ * PIDREF_MAKE_FROM_PID() above, which does not acquire a pidfd.) */
@@ -43,13 +77,13 @@ static inline int pidref_set_self(PidRef *pidref) {
return pidref_set_pid(pidref, 0); return pidref_set_pid(pidref, 0);
} }
bool pidref_is_self(const PidRef *pidref); bool pidref_is_self(PidRef *pidref);
void pidref_done(PidRef *pidref); void pidref_done(PidRef *pidref);
PidRef* pidref_free(PidRef *pidref); PidRef* pidref_free(PidRef *pidref);
DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free); 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_dup(const PidRef *pidref, PidRef **ret);
int pidref_new_from_pid(pid_t pid, 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_kill_and_sigcont(const PidRef *pidref, int sig);
int pidref_sigqueue(const PidRef *pidref, int sig, int value); int pidref_sigqueue(const PidRef *pidref, int sig, int value);
int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options); int pidref_wait(PidRef *pidref, siginfo_t *siginfo, int options);
int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret); int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret);
static inline void pidref_done_sigkill_wait(PidRef *pidref) { static inline void pidref_done_sigkill_wait(PidRef *pidref) {
if (!pidref_is_set(pidref)) if (!pidref_is_set(pidref))

View File

@@ -26,8 +26,7 @@ struct prioq_item {
struct Prioq { struct Prioq {
compare_func_t compare_func; compare_func_t compare_func;
unsigned n_items, n_allocated; unsigned n_items;
struct prioq_item *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) { int prioq_put(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k; unsigned k;
assert(q); assert(q);
if (q->n_items >= q->n_allocated) { if (!GREEDY_REALLOC(q->items, MAX(q->n_items + 1, 16u)))
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)
return -ENOMEM; return -ENOMEM;
q->items = j;
q->n_allocated = n;
}
k = q->n_items++; k = q->n_items++;
i = q->items + k; q->items[k] = (struct prioq_item) {
i->data = data; .data = data,
i->idx = idx; .idx = idx,
};
if (idx) if (idx)
*idx = k; *idx = k;

View File

@@ -27,6 +27,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "architecture.h" #include "architecture.h"
#include "argv-util.h" #include "argv-util.h"
#include "cgroup-util.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "env-file.h" #include "env-file.h"
#include "env-util.h" #include "env-util.h"
@@ -36,6 +37,7 @@
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "io-util.h"
#include "locale-util.h" #include "locale-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
@@ -48,6 +50,7 @@
#include "nulstr-util.h" #include "nulstr-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "pidfd-util.h"
#include "process-util.h" #include "process-util.h"
#include "raw-clone.h" #include "raw-clone.h"
#include "rlimit-util.h" #include "rlimit-util.h"
@@ -57,6 +60,7 @@
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "terminal-util.h" #include "terminal-util.h"
#include "time-util.h"
#include "user-util.h" #include "user-util.h"
#include "utf8.h" #include "utf8.h"
@@ -103,8 +107,8 @@ int pid_get_comm(pid_t pid, char **ret) {
_cleanup_free_ char *escaped = NULL, *comm = NULL; _cleanup_free_ char *escaped = NULL, *comm = NULL;
int r; int r;
assert(ret);
assert(pid >= 0); assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached()) { if (pid == 0 || pid == getpid_cached()) {
comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */ 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)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_comm(pid->pid, &comm); r = pid_get_comm(pid->pid, &comm);
if (r < 0) if (r < 0)
return r; return r;
@@ -290,6 +297,9 @@ int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlag
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_cmdline(pid->pid, max_columns, flags, &s); r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
if (r < 0) if (r < 0)
return r; return r;
@@ -332,6 +342,9 @@ int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char *
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_cmdline_strv(pid->pid, flags, &args); r = pid_get_cmdline_strv(pid->pid, flags, &args);
if (r < 0) if (r < 0)
return r; return r;
@@ -387,33 +400,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0; 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) { int pid_is_kernel_thread(pid_t pid) {
_cleanup_free_ char *line = NULL; _cleanup_free_ char *line = NULL;
unsigned long long flags; unsigned long long flags;
@@ -478,6 +464,9 @@ int pidref_is_kernel_thread(const PidRef *pid) {
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
result = pid_is_kernel_thread(pid->pid); result = pid_is_kernel_thread(pid->pid);
if (result < 0) if (result < 0)
return result; return result;
@@ -489,22 +478,6 @@ int pidref_is_kernel_thread(const PidRef *pid) {
return result; 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) { static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
const char *p; const char *p;
int r; 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) { int pidref_get_uid(const PidRef *pid, uid_t *ret) {
uid_t uid;
int r; int r;
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; 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); r = pid_get_uid(pid->pid, &uid);
if (r < 0) if (r < 0)
return r; return r;
@@ -680,7 +662,7 @@ int get_process_environ(pid_t pid, char **ret) {
return 0; 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; _cleanup_free_ char *line = NULL;
unsigned long ppid; unsigned long ppid;
const char *p; const char *p;
@@ -688,15 +670,17 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
assert(pid >= 0); 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) if (ret)
*ret = getppid(); *ret = getppid();
return 0; return 0;
} }
if (pid == 1) /* PID 1 has no parent, shortcut this case */
return -EADDRNOTAVAIL;
p = procfs_file_alloca(pid, "stat"); p = procfs_file_alloca(pid, "stat");
r = read_one_line_file(p, &line); r = read_one_line_file(p, &line);
if (r == -ENOENT) if (r == -ENOENT)
@@ -710,7 +694,6 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
p = strrchr(line, ')'); p = strrchr(line, ')');
if (!p) if (!p)
return -EIO; return -EIO;
p++; p++;
if (sscanf(p, " " if (sscanf(p, " "
@@ -719,9 +702,9 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
&ppid) != 1) &ppid) != 1)
return -EIO; return -EIO;
/* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for /* If ppid is zero the process has no parent. Which might be the case for PID 1 (caught above)
* processes originating in other namespaces that are inserted into a pidns. Return a recognizable * but also for processes originating in other namespaces that are inserted into a pidns.
* error in this case. */ * Return a recognizable error in this case. */
if (ppid == 0) if (ppid == 0)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
@@ -734,7 +717,74 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0; 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; _cleanup_free_ char *line = NULL;
const char *p; const char *p;
int r; int r;
@@ -754,7 +804,6 @@ int pid_get_start_time(pid_t pid, uint64_t *ret) {
p = strrchr(line, ')'); p = strrchr(line, ')');
if (!p) if (!p)
return -EIO; return -EIO;
p++; p++;
unsigned long llu; unsigned long llu;
@@ -784,18 +833,21 @@ int pid_get_start_time(pid_t pid, uint64_t *ret) {
return -EIO; return -EIO;
if (ret) if (ret)
*ret = llu; *ret = jiffies_to_usec(llu); /* CLOCK_BOOTTIME */
return 0; return 0;
} }
int pidref_get_start_time(const PidRef *pid, uint64_t *ret) { int pidref_get_start_time(const PidRef *pid, usec_t *ret) {
uint64_t t; usec_t t;
int r; int r;
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
r = pid_get_start_time(pid->pid, ret ? &t : NULL); r = pid_get_start_time(pid->pid, ret ? &t : NULL);
if (r < 0) if (r < 0)
return r; 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) { int getenv_for_pid(pid_t pid, const char *field, char **ret) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
const char *path; const char *path;
size_t sum = 0; size_t sum = 0;
int r; int r;
@@ -1035,22 +1086,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
assert(field); assert(field);
assert(ret); assert(ret);
if (pid == 0 || pid == getpid_cached()) { if (pid == 0 || pid == getpid_cached())
const char *e; return strdup_to_full(ret, getenv(field));
e = getenv(field);
if (!e) {
*ret = NULL;
return 0;
}
value = strdup(e);
if (!value)
return -ENOMEM;
*ret = value;
return 1;
}
if (!pid_is_valid(pid)) if (!pid_is_valid(pid))
return -EINVAL; return -EINVAL;
@@ -1079,78 +1116,55 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
sum += r; sum += r;
match = startswith(line, field); match = startswith(line, field);
if (match && *match == '=') { if (match && *match == '=')
value = strdup(match + 1); return strdup_to_full(ret, match + 1);
if (!value)
return -ENOMEM;
*ret = value;
return 1;
}
} }
*ret = NULL; *ret = NULL;
return 0; return 0;
} }
int pid_is_my_child(pid_t pid) { int pidref_is_my_child(PidRef *pid) {
pid_t ppid;
int r; int r;
if (pid < 0) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pid <= 1) if (pidref_is_remote(pid))
return -EREMOTE;
if (pid->pid == 1 || pidref_is_self(pid))
return false; 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) if (r < 0)
return r; return r;
return ppid == getpid_cached(); return ppid == getpid_cached();
} }
int pidref_is_my_child(const PidRef *pid) { int pid_is_my_child(pid_t pid) {
int r, result;
if (!pidref_is_set(pid)) if (pid == 0)
return -ESRCH; return false;
result = pid_is_my_child(pid->pid); return pidref_is_my_child(&PIDREF_MAKE_FROM_PID(pid));
if (result < 0)
return result;
r = pidref_verify(pid);
if (r < 0)
return r;
return result;
} }
int pid_is_unwaited(pid_t pid) { int pidref_is_unwaited(PidRef *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 r; int r;
/* Checks whether a PID is still valid at all, including a zombie */
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pid))
return -EREMOTE;
if (pid->pid == 1 || pidref_is_self(pid)) if (pid->pid == 1 || pidref_is_self(pid))
return true; return true;
@@ -1163,6 +1177,14 @@ int pidref_is_unwaited(const PidRef *pid) {
return true; 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 pid_is_alive(pid_t pid) {
int r; int r;
@@ -1192,6 +1214,9 @@ int pidref_is_alive(const PidRef *pidref) {
if (!pidref_is_set(pidref)) if (!pidref_is_set(pidref))
return -ESRCH; return -ESRCH;
if (pidref_is_remote(pidref))
return -EREMOTE;
result = pid_is_alive(pidref->pid); result = pid_is_alive(pidref->pid);
if (result < 0) { if (result < 0) {
assert(result != -ESRCH); assert(result != -ESRCH);
@@ -1207,28 +1232,63 @@ int pidref_is_alive(const PidRef *pidref) {
return result; return result;
} }
int pid_from_same_root_fs(pid_t pid) { int pidref_from_same_root_fs(PidRef *a, PidRef *b) {
const char *root; _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; return false;
if (pid == 0 || pid == getpid_cached()) if (pidref_equal(a, b))
return true; 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 */ #endif /* NM_IGNORED */
bool is_main_thread(void) { bool is_main_thread(void) {
static thread_local int cached = 0; static thread_local int cached = -1;
if (_unlikely_(cached == 0)) if (cached < 0)
cached = getpid_cached() == gettid() ? 1 : -1; cached = getpid_cached() == gettid();
return cached > 0; return cached;
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
@@ -1423,11 +1483,6 @@ int must_be_root(void) {
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); 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) { pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
size_t ps; size_t ps;
pid_t pid; pid_t pid;
@@ -1467,6 +1522,11 @@ pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
return pid; 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) { static int fork_flags_to_signal(ForkFlags flags) {
return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM : return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
(flags & FORK_DEATHSIG_SIGINT) ? SIGINT : (flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
@@ -1487,8 +1547,8 @@ int safe_fork_full(
bool block_signals = false, block_all = false, intermediary = false; bool block_signals = false, block_all = false, intermediary = false;
int prio, r; int prio, r;
assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid); assert(!FLAGS_SET(flags, FORK_DETACH) ||
assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT)); (!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 /* 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. */ * 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 (block_signals) {
if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) if (sigprocmask(SIG_BLOCK, &ss, &saved_ss) < 0)
return log_full_errno(prio, errno, "Failed to set signal mask: %m"); return log_full_errno(prio, errno, "Failed to block signal mask: %m");
saved_ssp = &saved_ss; saved_ssp = &saved_ss;
} }
if (FLAGS_SET(flags, FORK_DETACH)) { if (FLAGS_SET(flags, FORK_DETACH)) {
assert(!FLAGS_SET(flags, FORK_WAIT));
assert(!ret_pid);
/* Fork off intermediary child if needed */ /* Fork off intermediary child if needed */
r = is_reaper_process(); 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| pid = raw_clone(SIGCHLD|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) | (FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) | (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
(FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0)); (FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0) |
(FLAGS_SET(flags, FORK_NEW_PIDNS) ? CLONE_NEWPID : 0));
else else
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
@@ -1748,6 +1806,9 @@ int safe_fork_full(
} }
} }
if (FLAGS_SET(flags, FORK_FREEZE))
freeze();
if (ret_pid) if (ret_pid)
*ret_pid = getpid_cached(); *ret_pid = getpid_cached();
@@ -1765,12 +1826,16 @@ int pidref_safe_fork_full(
pid_t pid; pid_t pid;
int r, q; int r, q;
assert(!FLAGS_SET(flags, FORK_WAIT));
r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid); r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
if (r < 0) if (r < 0 || !ret_pid)
return r; 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); 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 */ 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); *ret_pid = PIDREF_MAKE_FROM_PID(pid);
@@ -1841,6 +1906,9 @@ int namespace_fork(
int set_oom_score_adjust(int value) { int set_oom_score_adjust(int value) {
char t[DECIMAL_STR_MAX(int)]; char t[DECIMAL_STR_MAX(int)];
if (!oom_score_adjust_is_valid(value))
return -EINVAL;
xsprintf(t, "%i", value); xsprintf(t, "%i", value);
return write_string_file("/proc/self/oom_score_adj", t, 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); delete_trailing_chars(t, WHITESPACE);
assert_se(safe_atoi(t, &a) >= 0); r = safe_atoi(t, &a);
assert_se(oom_score_adjust_is_valid(a)); if (r < 0)
return r;
if (!oom_score_adjust_is_valid(a))
return -ENODATA;
if (ret) if (ret)
*ret = a; *ret = a;
return 0; 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) { static int rlimit_to_nice(rlim_t limit) {
if (limit <= 1) if (limit <= 1)
return PRIO_MAX-1; /* i.e. 19 */ 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 setpriority_closest(int priority) {
int current, limit, saved_errno;
struct rlimit highest; struct rlimit highest;
int r, current, limit;
/* Try to set requested nice level */ /* 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; return 1;
if (!ERRNO_IS_NEG_PRIVILEGE(r))
/* Permission failed */ return r;
saved_errno = -errno;
if (!ERRNO_IS_PRIVILEGE(saved_errno))
return saved_errno;
errno = 0; errno = 0;
current = getpriority(PRIO_PROCESS, 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 * then the whole setpriority() system call is blocked to us, hence let's propagate the error
* right-away */ * right-away */
if (priority > current) if (priority > current)
return saved_errno; return r;
if (getrlimit(RLIMIT_NICE, &highest) < 0) if (getrlimit(RLIMIT_NICE, &highest) < 0)
return -errno; return -errno;
limit = rlimit_to_nice(highest.rlim_cur); limit = rlimit_to_nice(highest.rlim_cur);
/* We are already less nice than limit allows us */ /* Push to the allowed limit if we're higher than that. Note that we could also be less nice than
if (current < limit) { * limit allows us, but still higher than what's requested. In that case our current value is
log_debug("Cannot raise nice level, permissions and the resource limit do not allow it."); * the best choice. */
return 0; if (current > limit)
}
/* Push to the allowed limit */
if (setpriority(PRIO_PROCESS, 0, limit) < 0) if (setpriority(PRIO_PROCESS, 0, limit) < 0)
return -errno; 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; return 0;
} }
@@ -1992,7 +2005,8 @@ _noreturn_ void freeze(void) {
break; 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 (;;) for (;;)
pause(); pause();
} }
@@ -2064,7 +2078,7 @@ int posix_spawn_wrapper(
const char *cgroup, const char *cgroup,
PidRef *ret_pidref) { PidRef *ret_pidref) {
short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF; short flags = POSIX_SPAWN_SETSIGMASK;
posix_spawnattr_t attr; posix_spawnattr_t attr;
sigset_t mask; sigset_t mask;
int r; int r;
@@ -2075,7 +2089,7 @@ int posix_spawn_wrapper(
* issues. * issues.
* *
* Also, move the newly-created process into 'cgroup' through POSIX_SPAWN_SETCGROUP (clone3()) * 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 * returns 1: We're already in the right cgroup
* 0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller * 0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller
* needs to call 'cg_attach' on their own */ * 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; _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
#if HAVE_PIDFD_SPAWN #if HAVE_PIDFD_SPAWN
static bool have_clone_into_cgroup = true; /* kernel 5.7+ */
_cleanup_close_ int cgroup_fd = -EBADF; _cleanup_close_ int cgroup_fd = -EBADF;
if (cgroup) { if (cgroup && have_clone_into_cgroup) {
_cleanup_free_ char *resolved_cgroup = NULL; _cleanup_free_ char *resolved_cgroup = NULL;
r = cg_get_path_and_check( r = cg_get_path_and_check(
@@ -2130,25 +2145,38 @@ int posix_spawn_wrapper(
_cleanup_close_ int pidfd = -EBADF; _cleanup_close_ int pidfd = -EBADF;
r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp); 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)); r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
if (r < 0) if (r < 0)
return r; return r;
return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP); return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
} #else
if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
return -r;
/* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
* flags to remove the cgroup one, which is what redirects to clone3() */
flags &= ~POSIX_SPAWN_SETCGROUP;
r = posix_spawnattr_setflags(&attr, flags);
if (r != 0)
return -r;
#endif
pid_t pid; pid_t pid;
r = posix_spawn(&pid, path, NULL, &attr, argv, envp); r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
if (r != 0) if (r != 0)
return -r; return -r;
@@ -2158,6 +2186,7 @@ int posix_spawn_wrapper(
return r; return r;
return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */ 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) { 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); 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 */ #endif /* NM_IGNORED */

View File

@@ -14,7 +14,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "format-util.h" #include "format-util.h"
#include "macro.h" #include "macro.h"
#include "namespace-util.h" #include "pidref.h"
#include "time-util.h" #include "time-util.h"
#define procfs_file_alloca(pid, field) \ #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 pid_get_uid(pid_t pid, uid_t *ret);
int pidref_get_uid(const PidRef *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_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_cwd(pid_t pid, char **ret);
int get_process_root(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_environ(pid_t pid, char **ret);
int get_process_ppid(pid_t pid, pid_t *ret); int pid_get_ppid(pid_t pid, pid_t *ret);
int pid_get_start_time(pid_t pid, uint64_t *ret); int pidref_get_ppid(const PidRef *pidref, pid_t *ret);
int pidref_get_start_time(const PidRef* pid, uint64_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 get_process_umask(pid_t pid, mode_t *ret);
int container_get_leader(const char *machine, pid_t *pid); 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); int wait_for_terminate(pid_t pid, siginfo_t *status);
typedef enum WaitFlags { 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 pid_is_alive(pid_t pid);
int pidref_is_alive(const PidRef *pidref); int pidref_is_alive(const PidRef *pidref);
int pid_is_unwaited(pid_t pid); 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 pid_is_my_child(pid_t pid);
int pidref_is_my_child(const PidRef *pidref); int pidref_is_my_child(PidRef *pidref);
int pid_from_same_root_fs(pid_t pid); int pidref_from_same_root_fs(PidRef *a, PidRef *b);
bool is_main_thread(void); bool is_main_thread(void);
@@ -149,9 +148,15 @@ static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR); 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) { static inline bool pid_is_valid(pid_t p) {
return p > 0; return p > 0;
} }
static inline bool pid_is_automatic(pid_t p) {
return p == PID_AUTOMATIC;
}
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
pid_t getpid_cached(void); 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); 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, * 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 * 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 * 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_REOPEN_LOG = 1 << 6, /* Reopen log connection */
FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */ FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */
FORK_WAIT = 1 << 8, /* Wait until child exited */ 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 << 9, /* Make child's mount namespace MS_SLAVE */
FORK_MOUNTNS_SLAVE = 1 << 10, /* Make child's mount namespace MS_SLAVE */ FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
FORK_PRIVATE_TMP = 1 << 11, /* 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_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
FORK_STDOUT_TO_STDERR = 1 << 13, /* Make stdout a copy of stderr */ FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
FORK_FLUSH_STDIO = 1 << 14, /* 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_NEW_USERNS = 1 << 15, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_KEEP_NOTIFY_SOCKET = 1 << 15, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */ FORK_DETACH = 1 << 16, /* Double fork if needed to ensure PID1/subreaper is parent */
FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */ 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_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */ FORK_NEW_MOUNTNS = 1 << 18, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_NEW_NETNS = 1 << 19, /* Run child in its own network 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_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_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; } ForkFlags;
int safe_fork_full( 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 */ /* Like TAKE_PTR() but for pid_t, resetting them to 0 */
#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 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); int setpriority_closest(int priority);
_noreturn_ void freeze(void); _noreturn_ void freeze(void);
@@ -267,3 +271,6 @@ int posix_spawn_wrapper(
int proc_dir_open(DIR **ret); int proc_dir_open(DIR **ret);
int proc_dir_read(DIR *d, pid_t *ret); int proc_dir_read(DIR *d, pid_t *ret);
int proc_dir_read_pidref(DIR *d, PidRef *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 <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/auxv.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/time.h> #include <sys/time.h>
#if HAVE_SYS_AUXV_H
# include <sys/auxv.h>
#endif
#include "alloc-util.h" #include "alloc-util.h"
#include "env-util.h" #include "env-util.h"
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "io-util.h" #include "io-util.h"
#include "iovec-util.h"
#include "missing_random.h" #include "missing_random.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h" #include "missing_threads.h"
#include "parse-util.h" #include "parse-util.h"
#include "pidfd-util.h"
#include "process-util.h" #include "process-util.h"
#include "random-util.h" #include "random-util.h"
#include "sha256.h" #include "sha256.h"
@@ -43,10 +42,10 @@ static void fallback_random_bytes(void *p, size_t n) {
uint64_t call_id, block_id; uint64_t call_id, block_id;
usec_t stamp_mono, stamp_real; usec_t stamp_mono, stamp_real;
pid_t pid, tid; pid_t pid, tid;
uint64_t pidfdid;
uint8_t auxval[16]; uint8_t auxval[16];
} state = { } state = {
/* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */ /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
.label = "systemd fallback random bytes v1",
.call_id = fallback_counter++, .call_id = fallback_counter++,
.stamp_mono = now(CLOCK_MONOTONIC), .stamp_mono = now(CLOCK_MONOTONIC),
.stamp_real = now(CLOCK_REALTIME), .stamp_real = now(CLOCK_REALTIME),
@@ -54,9 +53,9 @@ static void fallback_random_bytes(void *p, size_t n) {
.tid = gettid(), .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)); 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) { while (n > 0) {
struct sha256_ctx ctx; 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) { void random_bytes(void *p, size_t n) {
static bool have_getrandom = true, have_grndinsecure = true; static bool have_grndinsecure = true;
_cleanup_close_ int fd = -EBADF;
assert(p || n == 0);
if (n == 0) if (n == 0)
return; return;
@@ -86,32 +86,26 @@ void random_bytes(void *p, size_t n) {
for (;;) { for (;;) {
ssize_t l; ssize_t l;
if (!have_getrandom)
break;
l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK); 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) if ((size_t) l == n)
return; /* Done reading, success. */ return; /* Done reading, success. */
p = (uint8_t *) p + l; p = (uint8_t *) p + l;
n -= l; n -= l;
continue; /* Interrupted by a signal; keep going. */ /* 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. */
} }
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); _cleanup_close_ int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0) if (fd >= 0 && loop_read_exact(fd, p, n, false) >= 0)
return; return;
/* This is a terrible fallback. Oh well. */ /* 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) { int crypto_random_bytes(void *p, size_t n) {
static bool have_getrandom = true, seen_initialized = false; assert(p || n == 0);
_cleanup_close_ int fd = -EBADF;
if (n == 0) if (n == 0)
return 0; return 0;
@@ -128,42 +121,37 @@ int crypto_random_bytes(void *p, size_t n) {
for (;;) { for (;;) {
ssize_t l; ssize_t l;
if (!have_getrandom)
break;
l = getrandom(p, n, 0); 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) if ((size_t) l == n)
return 0; /* Done reading, success. */ return 0; /* Done reading, success. */
p = (uint8_t *) p + l; p = (uint8_t *) p + l;
n -= l; n -= l;
continue; /* Interrupted by a signal; keep going. */ /* 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;
} }
if (!seen_initialized) { int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret) {
_cleanup_close_ int ready_fd = -EBADF; _cleanup_free_ void *p = NULL;
int r; int r;
ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); assert(ret);
if (ready_fd < 0)
return -errno; p = malloc(MAX(n, 1U));
r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY); if (!p)
return -ENOMEM;
r = crypto_random_bytes(p, n);
if (r < 0) if (r < 0)
return r; return r;
seen_initialized = true;
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); *ret = IOVEC_MAKE(TAKE_PTR(p), n);
if (fd < 0) return 0;
return -errno;
return loop_read_exact(fd, p, n, false);
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */

View File

@@ -4,9 +4,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.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. */ 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(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) { static inline uint64_t random_u64(void) {
uint64_t u; uint64_t u;

View File

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

View File

@@ -17,10 +17,6 @@
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int reset_all_signal_handlers(void) { int reset_all_signal_handlers(void) {
static const struct sigaction sa = {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
int ret = 0, r; int ret = 0, r;
for (int sig = 1; sig < _NSIG; sig++) { for (int sig = 1; sig < _NSIG; sig++) {
@@ -31,7 +27,7 @@ int reset_all_signal_handlers(void) {
/* On Linux the first two RT signals are reserved by glibc, and sigaction() will return /* On Linux the first two RT signals are reserved by glibc, and sigaction() will return
* EINVAL for them. */ * EINVAL for them. */
r = RET_NERRNO(sigaction(sig, &sa, NULL)); r = RET_NERRNO(sigaction(sig, &sigaction_default, NULL));
if (r != -EINVAL) if (r != -EINVAL)
RET_GATHER(ret, r); RET_GATHER(ret, r);
} }
@@ -271,7 +267,7 @@ int pop_pending_signal_internal(int sig, ...) {
if (r < 0) if (r < 0)
return r; return r;
r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }); r = sigtimedwait(&ss, NULL, &(const struct timespec) {});
if (r < 0) { if (r < 0) {
if (errno == EAGAIN) if (errno == EAGAIN)
return 0; return 0;
@@ -298,4 +294,35 @@ void propagate_signal(int sig, siginfo_t *siginfo) {
if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0) if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
assert_se(kill(p, sig) >= 0); assert_se(kill(p, sig) >= 0);
} }
const struct sigaction sigaction_ignore = {
.sa_handler = SIG_IGN,
.sa_flags = SA_RESTART,
};
const struct sigaction sigaction_default = {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
const struct sigaction sigaction_nop_nocldstop = {
.sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP|SA_RESTART,
};
int parse_signo(const char *s, int *ret) {
int sig, r;
r = safe_atoi(s, &sig);
if (r < 0)
return r;
if (!SIGNAL_VALID(sig))
return -EINVAL;
if (ret)
*ret = sig;
return 0;
}
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View File

@@ -12,19 +12,13 @@ int sigaction_many_internal(const struct sigaction *sa, ...);
#define ignore_signals(...) \ #define ignore_signals(...) \
sigaction_many_internal( \ sigaction_many_internal( \
&(const struct sigaction) { \ &sigaction_ignore, \
.sa_handler = SIG_IGN, \
.sa_flags = SA_RESTART \
}, \
__VA_ARGS__, \ __VA_ARGS__, \
-1) -1)
#define default_signals(...) \ #define default_signals(...) \
sigaction_many_internal( \ sigaction_many_internal( \
&(const struct sigaction) { \ &sigaction_default, \
.sa_handler = SIG_DFL, \
.sa_flags = SA_RESTART \
}, \
__VA_ARGS__, \ __VA_ARGS__, \
-1) -1)
@@ -34,7 +28,7 @@ int sigaction_many_internal(const struct sigaction *sa, ...);
int sigset_add_many_internal(sigset_t *ss, ...); int sigset_add_many_internal(sigset_t *ss, ...);
#define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1) #define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1)
int sigprocmask_many_internal(int how, sigset_t *old, ...); int sigprocmask_many_internal(int how, sigset_t *ret_old_mask, ...);
#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1) #define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1)
const char* signal_to_string(int i) _const_; const char* signal_to_string(int i) _const_;
@@ -52,6 +46,7 @@ static inline void block_signals_reset(sigset_t *ss) {
assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0); \ assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0); \
_t; \ _t; \
}) })
#define SIGNO_INVALID (-EINVAL)
static inline bool SIGNAL_VALID(int signo) { static inline bool SIGNAL_VALID(int signo) {
return signo > 0 && signo < _NSIG; return signo > 0 && signo < _NSIG;
@@ -70,3 +65,9 @@ int pop_pending_signal_internal(int sig, ...);
#define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1) #define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1)
void propagate_signal(int sig, siginfo_t *siginfo); void propagate_signal(int sig, siginfo_t *siginfo);
extern const struct sigaction sigaction_ignore;
extern const struct sigaction sigaction_default;
extern const struct sigaction sigaction_nop_nocldstop;
int parse_signo(const char *s, int *ret);

View File

@@ -2,10 +2,11 @@
#include "nm-sd-adapt-shared.h" #include "nm-sd-adapt-shared.h"
/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <net/if.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <poll.h> #include <poll.h>
@@ -24,7 +25,7 @@
#include "escape.h" #include "escape.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "format-util.h" #include "format-ifname.h"
#include "io-util.h" #include "io-util.h"
#include "log.h" #include "log.h"
#include "memory-util.h" #include "memory-util.h"
@@ -458,6 +459,7 @@ int sockaddr_pretty(
assert(sa); assert(sa);
assert(salen >= sizeof(sa->sa.sa_family)); assert(salen >= sizeof(sa->sa.sa_family));
assert(ret);
switch (sa->sa.sa_family) { switch (sa->sa.sa_family) {
@@ -638,7 +640,8 @@ int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret)
int r; int r;
assert(sa); assert(sa);
assert(salen > sizeof(sa_family_t)); assert(salen >= sizeof(sa_family_t));
assert(ret);
r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS); r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
if (r != 0) { if (r != 0) {
@@ -652,15 +655,7 @@ int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret)
return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret); return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
} }
if (ret) { return strdup_to(ret, host);
char *copy = strdup(host);
if (!copy)
return -ENOMEM;
*ret = copy;
}
return 0;
} }
static const char* const netlink_family_table[] = { static const char* const netlink_family_table[] = {
@@ -987,6 +982,28 @@ int getpeerpidfd(int fd) {
return pidfd; return pidfd;
} }
int getpeerpidref(int fd, PidRef *ret) {
int r;
assert(fd >= 0);
assert(ret);
int pidfd = getpeerpidfd(fd);
if (pidfd < 0) {
if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd))
return pidfd;
struct ucred ucred;
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
return pidref_set_pid(ret, ucred.pid);
}
return pidref_set_pidfd_consume(ret, pidfd);
}
ssize_t send_many_fds_iov_sa( ssize_t send_many_fds_iov_sa(
int transport_fd, int transport_fd,
int *fds_array, size_t n_fds_array, int *fds_array, size_t n_fds_array,
@@ -1124,14 +1141,10 @@ ssize_t receive_many_fds_iov(
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n); if (!GREEDY_REALLOC_APPEND(fds_array, n_fds_array, CMSG_TYPED_DATA(cmsg, int), n)) {
if (!fds_array) {
cmsg_close_all(&mh); cmsg_close_all(&mh);
return -ENOMEM; return -ENOMEM;
} }
memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n);
n_fds_array += n;
} }
if (n_fds_array == 0) { if (n_fds_array == 0) {
@@ -1468,18 +1481,22 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
ssize_t n; ssize_t n;
/* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably /* A wrapper around recvmsg() that checks for MSG_CTRUNC and MSG_TRUNC, and turns them into an error,
* safe way, closing any SCM_RIGHTS fds in the error path. * in a reasonably safe way, closing any received fds in the error path.
* *
* Note that unlike our usual coding style this might modify *msg on failure. */ * Note that unlike our usual coding style this might modify *msg on failure. */
assert(sockfd >= 0);
assert(msg);
n = recvmsg(sockfd, msg, flags); n = recvmsg(sockfd, msg, flags);
if (n < 0) if (n < 0)
return -errno; return -errno;
if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) { if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC) ||
(!FLAGS_SET(flags, MSG_PEEK) && FLAGS_SET(msg->msg_flags, MSG_TRUNC))) {
cmsg_close_all(msg); cmsg_close_all(msg);
return -EXFULL; /* a recognizable error code */ return FLAGS_SET(msg->msg_flags, MSG_CTRUNC) ? -ECHRNG : -EXFULL;
} }
return n; return n;
@@ -1781,15 +1798,49 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
int vsock_get_local_cid(unsigned *ret) { int vsock_get_local_cid(unsigned *ret) {
_cleanup_close_ int vsock_fd = -EBADF; _cleanup_close_ int vsock_fd = -EBADF;
assert(ret);
vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC); vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC);
if (vsock_fd < 0) if (vsock_fd < 0)
return log_debug_errno(errno, "Failed to open /dev/vsock: %m"); return log_debug_errno(errno, "Failed to open /dev/vsock: %m");
if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0) unsigned tmp;
if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret ?: &tmp) < 0)
return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m"); return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m");
return 0; return 0;
} }
int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups) {
_cleanup_free_ uint32_t *groups = NULL;
socklen_t len = 0, old_len;
assert(fd >= 0);
/* This returns ENOPROTOOPT if the kernel is older than 4.2. */
if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0)
return -errno;
if (len == 0)
goto finalize;
groups = new0(uint32_t, len);
if (!groups)
return -ENOMEM;
old_len = len;
if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len) < 0)
return -errno;
if (old_len != len)
return -EIO;
finalize:
if (ret_len)
*ret_len = len;
if (ret_groups)
*ret_groups = TAKE_PTR(groups);
return 0;
}
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View File

@@ -2,15 +2,16 @@
#pragma once #pragma once
#include <inttypes.h> #include <inttypes.h>
#include <linux/netlink.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/if_infiniband.h> #include <linux/if_infiniband.h>
#include <linux/if_packet.h> #include <linux/if_packet.h>
#include <linux/netlink.h>
#include <sys/socket.h> /* linux/vms_sockets.h requires 'struct sockaddr' */
#include <linux/vm_sockets.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
@@ -19,6 +20,7 @@
#include "macro.h" #include "macro.h"
#include "missing_network.h" #include "missing_network.h"
#include "missing_socket.h" #include "missing_socket.h"
#include "pidref.h"
#include "sparse-endian.h" #include "sparse-endian.h"
union sockaddr_union { union sockaddr_union {
@@ -28,7 +30,7 @@ union sockaddr_union {
/* The libc provided version that allocates "enough room" for every protocol */ /* The libc provided version that allocates "enough room" for every protocol */
struct sockaddr_storage storage; struct sockaddr_storage storage;
/* Protoctol-specific implementations */ /* Protocol-specific implementations */
struct sockaddr_in in; struct sockaddr_in in;
struct sockaddr_in6 in6; struct sockaddr_in6 in6;
struct sockaddr_un un; struct sockaddr_un un;
@@ -155,6 +157,7 @@ int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret); int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret); int getpeergroups(int fd, gid_t **ret);
int getpeerpidfd(int fd); int getpeerpidfd(int fd);
int getpeerpidref(int fd, PidRef *ret);
ssize_t send_many_fds_iov_sa( ssize_t send_many_fds_iov_sa(
int transport_fd, int transport_fd,
@@ -331,7 +334,7 @@ struct timespec_large {
/* glibc duplicates timespec/timeval on certain 32-bit arches, once in 32-bit and once in 64-bit. /* glibc duplicates timespec/timeval on certain 32-bit arches, once in 32-bit and once in 64-bit.
* See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them * See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
* to prevent from recvmsg_safe() returning -EXFULL. */ * to prevent truncating control msg (recvmsg() MSG_CTRUNC). */
#define CMSG_SPACE_TIMEVAL \ #define CMSG_SPACE_TIMEVAL \
((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \ ((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \
CMSG_SPACE(sizeof(struct timeval)) : \ CMSG_SPACE(sizeof(struct timeval)) : \
@@ -393,10 +396,12 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s);
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s); int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
/* libc's SOMAXCONN is defined to 128 or 4096 (at least on glibc). But actually, the value can be much /* libc's SOMAXCONN is defined to 128 or 4096 (at least on glibc). But actually, the value can be much
* larger. In our codebase we want to set it to the max usually, since noawadays socket memory is properly * larger. In our codebase we want to set it to the max usually, since nowadays socket memory is properly
* tracked by memcg, and hence we don't need to enforce extra limits here. Moreover, the kernel caps it to * tracked by memcg, and hence we don't need to enforce extra limits here. Moreover, the kernel caps it to
* /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file * /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
* authoritative. */ * authoritative. */
#define SOMAXCONN_DELUXE INT_MAX #define SOMAXCONN_DELUXE INT_MAX
int vsock_get_local_cid(unsigned *ret); int vsock_get_local_cid(unsigned *ret);
int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups);

View File

@@ -22,6 +22,7 @@
#include "missing_fs.h" #include "missing_fs.h"
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "mountpoint-util.h"
#include "nulstr-util.h" #include "nulstr-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "stat-util.h" #include "stat-util.h"
@@ -33,6 +34,7 @@ static int verify_stat_at(
bool follow, bool follow,
int (*verify_func)(const struct stat *st), int (*verify_func)(const struct stat *st),
bool verify) { bool verify) {
struct stat st; struct stat st;
int r; int r;
@@ -158,25 +160,9 @@ int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup)
struct dirent *buf; struct dirent *buf;
size_t m; size_t m;
if (path) { fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else if (dir_fd == AT_FDCWD) {
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else {
/* Note that DUPing is not enough, as the internal pointer would still be shared and moved
* getedents64(). */
assert(dir_fd >= 0);
fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0) if (fd < 0)
return fd; return fd;
}
/* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." + /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
@@ -231,7 +217,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
* When looking under root_dir, we can't expect /dev/ to be mounted, * When looking under root_dir, we can't expect /dev/ to be mounted,
* so let's see if the path is a (possibly dangling) symlink to /dev/null. */ * so let's see if the path is a (possibly dangling) symlink to /dev/null. */
if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null")) if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
return true; return true;
r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st); r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
@@ -241,7 +227,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
return null_or_empty(&st); return null_or_empty(&st);
} }
static int fd_is_read_only_fs(int fd) { int fd_is_read_only_fs(int fd) {
struct statvfs st; struct statvfs st;
assert(fd >= 0); assert(fd >= 0);
@@ -271,23 +257,109 @@ int path_is_read_only_fs(const char *path) {
return fd_is_read_only_fs(fd); return fd_is_read_only_fs(fd);
} }
#endif /* NM_IGNORED */
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags) { int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags) {
struct stat a, b; struct stat sta, stb;
int r;
assert(fda >= 0 || fda == AT_FDCWD); assert(fda >= 0 || fda == AT_FDCWD);
assert(fdb >= 0 || fdb == AT_FDCWD); assert(fdb >= 0 || fdb == AT_FDCWD);
assert((flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT)) == 0);
if (fstatat(fda, strempty(filea), &a, flags) < 0) /* Refuse an unset filea or fileb early unless AT_EMPTY_PATH is set */
return log_debug_errno(errno, "Cannot stat %s: %m", filea); if ((isempty(filea) || isempty(fileb)) && !FLAGS_SET(flags, AT_EMPTY_PATH))
return -EINVAL;
if (fstatat(fdb, strempty(fileb), &b, flags) < 0) /* Shortcut: comparing the same fd with itself means we can return true */
return log_debug_errno(errno, "Cannot stat %s: %m", fileb); if (fda >= 0 && fda == fdb && isempty(filea) && isempty(fileb) && FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW))
return true;
return stat_inode_same(&a, &b); _cleanup_close_ int pin_a = -EBADF, pin_b = -EBADF;
if (!FLAGS_SET(flags, AT_NO_AUTOMOUNT)) {
/* Let's try to use the name_to_handle_at() AT_HANDLE_FID API to identify identical
* inodes. We have to issue multiple calls on the same file for that (first, to acquire the
* FID, and then to check if .st_dev is actually the same). Hence let's pin the inode in
* between via O_PATH, unless we already have an fd for it. */
if (!isempty(filea)) {
pin_a = openat(fda, filea, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
if (pin_a < 0)
return -errno;
fda = pin_a;
filea = NULL;
flags |= AT_EMPTY_PATH;
} }
if (!isempty(fileb)) {
pin_b = openat(fdb, fileb, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
if (pin_b < 0)
return -errno;
fdb = pin_b;
fileb = NULL;
flags |= AT_EMPTY_PATH;
}
int ntha_flags = (flags & AT_EMPTY_PATH) | (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? 0 : AT_SYMLINK_FOLLOW);
_cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
int mntida = -1, mntidb = -1;
r = name_to_handle_at_try_fid(
fda,
filea,
&ha,
&mntida,
ntha_flags);
if (r < 0) {
if (is_name_to_handle_at_fatal_error(r))
return r;
goto fallback;
}
r = name_to_handle_at_try_fid(
fdb,
fileb,
&hb,
&mntidb,
ntha_flags);
if (r < 0) {
if (is_name_to_handle_at_fatal_error(r))
return r;
goto fallback;
}
/* Now compare the two file handles */
if (!file_handle_equal(ha, hb))
return false;
/* If the file handles are the same and they come from the same mount ID? Great, then we are
* good, they are definitely the same */
if (mntida == mntidb)
return true;
/* File handles are the same, they are not on the same mount id. This might either be because
* they are on two entirely different file systems, that just happen to have the same FIDs
* (because they originally where created off the same disk images), or it could be because
* they are located on two distinct bind mounts of the same fs. To check that, let's look at
* .st_rdev of the inode. We simply reuse the fallback codepath for that, since it checks
* exactly that (it checks slightly more, but we don't care.) */
}
fallback:
if (fstatat(fda, strempty(filea), &sta, flags) < 0)
return log_debug_errno(errno, "Cannot stat %s: %m", strna(filea));
if (fstatat(fdb, strempty(fileb), &stb, flags) < 0)
return log_debug_errno(errno, "Cannot stat %s: %m", strna(fileb));
return stat_inode_same(&sta, &stb);
}
#endif /* NM_IGNORED */
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
assert(s); assert(s);
assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
@@ -369,8 +441,7 @@ bool stat_inode_same(const struct stat *a, const struct stat *b) {
/* Returns if the specified stat structure references the same (though possibly modified) inode. Does /* Returns if the specified stat structure references the same (though possibly modified) inode. Does
* a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */ * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */
return a && b && return stat_is_set(a) && stat_is_set(b) &&
(a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
a->st_dev == b->st_dev && a->st_dev == b->st_dev &&
a->st_ino == b->st_ino; a->st_ino == b->st_ino;
@@ -399,9 +470,8 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
/* Same as stat_inode_same() but for struct statx */ /* Same as stat_inode_same() but for struct statx */
return a && b && return statx_is_set(a) && statx_is_set(b) &&
FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) &&
(a->stx_mode & S_IFMT) != 0 &&
((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 && ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
a->stx_dev_major == b->stx_dev_major && a->stx_dev_major == b->stx_dev_major &&
a->stx_dev_minor == b->stx_dev_minor && a->stx_dev_minor == b->stx_dev_minor &&
@@ -409,7 +479,7 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
} }
bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) { bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
if (!a || !b) if (!new_statx_is_set(a) || !new_statx_is_set(b))
return false; return false;
/* if we have the mount ID, that's all we need */ /* if we have the mount ID, that's all we need */
@@ -545,6 +615,8 @@ const char* inode_type_to_string(mode_t m) {
return "sock"; return "sock";
} }
/* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
* return an .st_mode where we'll return NULL here! */
return NULL; return NULL;
} }

View File

@@ -9,6 +9,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/vfs.h> #include <sys/vfs.h>
#include "fs-util.h"
#include "macro.h" #include "macro.h"
#include "missing_stat.h" #include "missing_stat.h"
#include "siphash24.h" #include "siphash24.h"
@@ -44,13 +45,16 @@ static inline int null_or_empty_path(const char *fn) {
return null_or_empty_path_with_root(fn, NULL); return null_or_empty_path_with_root(fn, NULL);
} }
int fd_is_read_only_fs(int fd);
int path_is_read_only_fs(const char *path); int path_is_read_only_fs(const char *path);
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags); int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags);
static inline int inode_same(const char *filea, const char *fileb, int flags) { static inline int inode_same(const char *filea, const char *fileb, int flags) {
return inode_same_at(AT_FDCWD, filea, AT_FDCWD, fileb, flags); return inode_same_at(AT_FDCWD, filea, AT_FDCWD, fileb, flags);
} }
static inline int fd_inode_same(int fda, int fdb) {
return inode_same_at(fda, NULL, fdb, NULL, AT_EMPTY_PATH);
}
/* The .f_type field of struct statfs is really weird defined on /* The .f_type field of struct statfs is really weird defined on
* different archs. Let's give its type a name. */ * different archs. Let's give its type a name. */
@@ -126,3 +130,20 @@ extern const struct hash_ops inode_hash_ops;
const char* inode_type_to_string(mode_t m); const char* inode_type_to_string(mode_t m);
mode_t inode_type_from_string(const char *s); mode_t inode_type_from_string(const char *s);
/* Macros that check whether the stat/statx structures have been initialized already. For "struct stat" we
* use a check for .st_dev being non-zero, since the kernel unconditionally fills that in, mapping the file
* to its originating superblock, regardless if the fs is block based or virtual (we also check for .st_mode
* being MODE_INVALID, since we use that as an invalid marker for separate mode_t fields). For "struct statx"
* we use the .stx_mask field, which must be non-zero if any of the fields have already been initialized. */
static inline bool stat_is_set(const struct stat *st) {
return st && st->st_dev != 0 && st->st_mode != MODE_INVALID;
}
#if 0 /* NM_IGNORED */
static inline bool statx_is_set(const struct statx *sx) {
return sx && sx->stx_mask != 0;
}
static inline bool new_statx_is_set(const struct new_statx *sx) {
return sx && sx->stx_mask != 0;
}
#endif /* NM_IGNORED */

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