merge: branch 'systemd' into main
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2115
This commit is contained in:
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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)
|
||||||
|
@@ -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,
|
||||||
|
@@ -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"
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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 ? © : NULL, 0) < 0) {
|
if (pidfd_send_signal(s->child.pidfd, sig, si ? © : 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;
|
||||||
}
|
}
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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, \
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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',
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* dummy header */
|
3
src/libnm-systemd-shared/sd-adapt-shared/keyring-util.h
Normal file
3
src/libnm-systemd-shared/sd-adapt-shared/keyring-util.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* dummy header */
|
@@ -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
|
||||||
|
3
src/libnm-systemd-shared/sd-adapt-shared/syslog-util.h
Normal file
3
src/libnm-systemd-shared/sd-adapt-shared/syslog-util.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* dummy header */
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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) \
|
||||||
({ \
|
({ \
|
||||||
|
73
src/libnm-systemd-shared/src/basic/bitfield.h
Normal file
73
src/libnm-systemd-shared/src/basic/bitfield.h
Normal 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))
|
@@ -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)
|
||||||
|
@@ -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);
|
||||||
|
176
src/libnm-systemd-shared/src/basic/chattr-util.c
Normal file
176
src/libnm-systemd-shared/src/basic/chattr-util.c
Normal 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, ¤t_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);
|
||||||
|
}
|
64
src/libnm-systemd-shared/src/basic/chattr-util.h
Normal file
64
src/libnm-systemd-shared/src/basic/chattr-util.h
Normal 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);
|
||||||
|
}
|
@@ -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"
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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);
|
||||||
|
@@ -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,
|
||||||
|
@@ -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;
|
||||||
|
@@ -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. */
|
||||||
|
}
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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);
|
||||||
|
39
src/libnm-systemd-shared/src/basic/format-ifname.c
Normal file
39
src/libnm-systemd-shared/src/basic/format-ifname.c
Normal 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);
|
||||||
|
}
|
27
src/libnm-systemd-shared/src/basic/format-ifname.h
Normal file
27
src/libnm-systemd-shared/src/basic/format-ifname.h
Normal 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]){})
|
@@ -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,
|
||||||
|
@@ -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,
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
@@ -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"🪪",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
@@ -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));
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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,
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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__))
|
||||||
|
|
||||||
|
@@ -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) \
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
48
src/libnm-systemd-shared/src/basic/missing_pidfd.h
Normal file
48
src/libnm-systemd-shared/src/basic/missing_pidfd.h
Normal 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
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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
|
||||||
|
12
src/libnm-systemd-shared/src/basic/missing_wait.h
Normal file
12
src/libnm-systemd-shared/src/basic/missing_wait.h
Normal 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
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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);
|
||||||
|
279
src/libnm-systemd-shared/src/basic/pidfd-util.c
Normal file
279
src/libnm-systemd-shared/src/basic/pidfd-util.c
Normal 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, ¤t_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;
|
||||||
|
}
|
21
src/libnm-systemd-shared/src/basic/pidfd-util.h
Normal file
21
src/libnm-systemd-shared/src/basic/pidfd-util.h
Normal 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);
|
@@ -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))
|
||||||
|
@@ -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;
|
||||||
|
@@ -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, ¤t_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 */
|
||||||
|
@@ -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);
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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;
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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);
|
||||||
|
52
src/libnm-systemd-shared/src/basic/sha256.c
Normal file
52
src/libnm-systemd-shared/src/basic/sha256.c
Normal 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;
|
||||||
|
}
|
16
src/libnm-systemd-shared/src/basic/sha256.h
Normal file
16
src/libnm-systemd-shared/src/basic/sha256.h
Normal 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);
|
||||||
|
}
|
@@ -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 */
|
||||||
|
@@ -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);
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
Reference in New Issue
Block a user