mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-03 16:20:14 +01:00
systemd: merge branch systemd into main
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/945
This commit is contained in:
commit
ce3069b01e
42 changed files with 992 additions and 1069 deletions
|
|
@ -61,6 +61,8 @@
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define HAVE_OPENSSL 0
|
||||
|
||||
static inline int
|
||||
sd_notify(int unset_environment, const char *state)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@
|
|||
#include <netinet/if_ether.h>
|
||||
|
||||
#include "arp-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "unaligned.h"
|
||||
#include "util.h"
|
||||
|
||||
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
|
||||
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) {
|
||||
struct sock_filter filter[] = {
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
|
||||
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
|
||||
|
|
@ -48,13 +50,13 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
|
|||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
/* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
|
||||
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
|
||||
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
|
||||
|
|
@ -63,15 +65,25 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
|
|||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
};
|
||||
struct sock_fprog fprog = {
|
||||
.len = ELEMENTSOF(filter),
|
||||
.filter = (struct sock_filter*) filter
|
||||
.len = ELEMENTSOF(filter),
|
||||
.filter = (struct sock_filter*) filter,
|
||||
};
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) {
|
||||
union sockaddr_union link = {
|
||||
.ll.sll_family = AF_PACKET,
|
||||
.ll.sll_family = AF_PACKET,
|
||||
.ll.sll_protocol = htobe16(ETH_P_ARP),
|
||||
.ll.sll_ifindex = ifindex,
|
||||
.ll.sll_halen = ETH_ALEN,
|
||||
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
.ll.sll_ifindex = ifindex,
|
||||
.ll.sll_halen = ETH_ALEN,
|
||||
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
};
|
||||
_cleanup_close_ int s = -1;
|
||||
int r;
|
||||
|
|
@ -82,59 +94,57 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
|
|||
if (s < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
|
||||
r = arp_update_filter(s, a, eth_mac);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
return r;
|
||||
|
||||
r = bind(s, &link.sa, sizeof(link.ll));
|
||||
if (r < 0)
|
||||
if (bind(s, &link.sa, sizeof(link.ll)) < 0)
|
||||
return -errno;
|
||||
|
||||
return TAKE_FD(s);
|
||||
}
|
||||
|
||||
static int arp_send_packet(int fd, int ifindex,
|
||||
be32_t pa, const struct ether_addr *ha,
|
||||
bool announce) {
|
||||
int arp_send_packet(
|
||||
int fd,
|
||||
int ifindex,
|
||||
const struct in_addr *pa,
|
||||
const struct ether_addr *ha,
|
||||
bool announce) {
|
||||
|
||||
union sockaddr_union link = {
|
||||
.ll.sll_family = AF_PACKET,
|
||||
.ll.sll_family = AF_PACKET,
|
||||
.ll.sll_protocol = htobe16(ETH_P_ARP),
|
||||
.ll.sll_ifindex = ifindex,
|
||||
.ll.sll_halen = ETH_ALEN,
|
||||
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
.ll.sll_ifindex = ifindex,
|
||||
.ll.sll_halen = ETH_ALEN,
|
||||
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
};
|
||||
struct ether_arp arp = {
|
||||
.ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
|
||||
.ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
|
||||
.ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
|
||||
.ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
|
||||
.ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
|
||||
.ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
|
||||
.ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
|
||||
.ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
|
||||
.ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */
|
||||
.ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
|
||||
};
|
||||
int r;
|
||||
ssize_t n;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(pa != 0);
|
||||
assert(ifindex > 0);
|
||||
assert(pa);
|
||||
assert(in4_addr_is_set(pa));
|
||||
assert(ha);
|
||||
assert(!ether_addr_is_null(ha));
|
||||
|
||||
memcpy(&arp.arp_sha, ha, ETH_ALEN);
|
||||
memcpy(&arp.arp_tpa, &pa, sizeof(pa));
|
||||
memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr));
|
||||
|
||||
if (announce)
|
||||
memcpy(&arp.arp_spa, &pa, sizeof(pa));
|
||||
memcpy(&arp.arp_spa, pa, sizeof(struct in_addr));
|
||||
|
||||
r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
|
||||
if (r < 0)
|
||||
n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n != sizeof(struct ether_arp))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arp_send_probe(int fd, int ifindex,
|
||||
be32_t pa, const struct ether_addr *ha) {
|
||||
return arp_send_packet(fd, ifindex, pa, ha, false);
|
||||
}
|
||||
|
||||
int arp_send_announcement(int fd, int ifindex,
|
||||
be32_t pa, const struct ether_addr *ha) {
|
||||
return arp_send_packet(fd, ifindex, pa, ha, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,31 @@
|
|||
***/
|
||||
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "socket-util.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
|
||||
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac);
|
||||
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac);
|
||||
|
||||
int arp_send_probe(int fd, int ifindex,
|
||||
be32_t pa, const struct ether_addr *ha);
|
||||
int arp_send_announcement(int fd, int ifindex,
|
||||
be32_t pa, const struct ether_addr *ha);
|
||||
int arp_send_packet(
|
||||
int fd,
|
||||
int ifindex,
|
||||
const struct in_addr *pa,
|
||||
const struct ether_addr *ha,
|
||||
bool announce);
|
||||
static inline int arp_send_probe(
|
||||
int fd,
|
||||
int ifindex,
|
||||
const struct in_addr *pa,
|
||||
const struct ether_addr *ha) {
|
||||
return arp_send_packet(fd, ifindex, pa, ha, false);
|
||||
}
|
||||
static inline int arp_send_announcement(
|
||||
int fd,
|
||||
int ifindex,
|
||||
const struct in_addr *pa,
|
||||
const struct ether_addr *ha) {
|
||||
return arp_send_packet(fd, ifindex, pa, ha, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ int sd_dhcp_client_set_callback(
|
|||
int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->request_broadcast = !!broadcast;
|
||||
client->request_broadcast = broadcast;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1592,18 +1592,17 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
|
|||
if (r != DHCP_FORCERENEW)
|
||||
return -ENOMSG;
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
#if 0
|
||||
log_dhcp_client(client, "FORCERENEW");
|
||||
|
||||
return 0;
|
||||
#else /* NM_IGNORED */
|
||||
/* NM: patch out the handling of FORCERENEW. We don't implement rfc3118 (Authentication
|
||||
* for DHCP Messages) nor rfc6704 (Forcerenew Nonce Authentication) so accepting
|
||||
* unauthenticated FORCERENEW requests is a security issue (CVE-2020-13529)
|
||||
* See: https://github.com/systemd/systemd/issues/16774 */
|
||||
log_dhcp_client(client, "ignore FORCERENEW");
|
||||
#else
|
||||
/* FIXME: Ignore FORCERENEW requests until we implement RFC3118 (Authentication for DHCP
|
||||
* Messages) and/or RFC6704 (Forcerenew Nonce Authentication), as unauthenticated FORCERENEW
|
||||
* requests causes a security issue (TALOS-2020-1142, CVE-2020-13529). */
|
||||
log_dhcp_client(client, "Received FORCERENEW, ignoring.");
|
||||
return -ENOMSG;
|
||||
#endif /* NM_IGNORED */
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
|
||||
|
|
@ -1692,7 +1691,6 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
|
|||
|
||||
static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
||||
usec_t time_now;
|
||||
char time_string[FORMAT_TIMESPAN_MAX];
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
|
|
@ -1754,7 +1752,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
return 0;
|
||||
|
||||
log_dhcp_client(client, "lease expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->expire_time - time_now, USEC_PER_SEC));
|
||||
FORMAT_TIMESPAN(client->expire_time - time_now, USEC_PER_SEC));
|
||||
|
||||
/* arm T2 timeout */
|
||||
r = event_reset_time(client->event, &client->timeout_t2,
|
||||
|
|
@ -1770,7 +1768,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
return 0;
|
||||
|
||||
log_dhcp_client(client, "T2 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t2_time - time_now, USEC_PER_SEC));
|
||||
FORMAT_TIMESPAN(client->t2_time - time_now, USEC_PER_SEC));
|
||||
|
||||
/* arm T1 timeout */
|
||||
r = event_reset_time(client->event, &client->timeout_t1,
|
||||
|
|
@ -1783,15 +1781,14 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
|
||||
if (client->t1_time > time_now)
|
||||
log_dhcp_client(client, "T1 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t1_time - time_now, USEC_PER_SEC));
|
||||
FORMAT_TIMESPAN(client->t1_time - time_now, USEC_PER_SEC));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
|
||||
DHCP_CLIENT_DONT_DESTROY(client);
|
||||
char time_string[FORMAT_TIMESPAN_MAX];
|
||||
int r = 0, notify_event = 0;
|
||||
int r, notify_event;
|
||||
|
||||
assert(client);
|
||||
assert(client->event);
|
||||
|
|
@ -1801,22 +1798,19 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
case DHCP_STATE_SELECTING:
|
||||
|
||||
r = client_handle_offer(client, message, len);
|
||||
if (r >= 0) {
|
||||
if (r == -ENOMSG)
|
||||
return 0; /* invalid message, let's ignore it */
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
client->state = DHCP_STATE_REQUESTING;
|
||||
client->attempt = 0;
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
clock_boottime_or_monotonic(),
|
||||
0, 0,
|
||||
client_timeout_resend, client,
|
||||
client->event_priority, "dhcp4-resend-timer", true);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
} else if (r == -ENOMSG)
|
||||
/* invalid message, let's ignore it */
|
||||
return 0;
|
||||
client->state = DHCP_STATE_REQUESTING;
|
||||
client->attempt = 0;
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
clock_boottime_or_monotonic(),
|
||||
0, 0,
|
||||
client_timeout_resend, client,
|
||||
client->event_priority, "dhcp4-resend-timer", true);
|
||||
break;
|
||||
|
||||
case DHCP_STATE_REBOOTING:
|
||||
|
|
@ -1825,47 +1819,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
case DHCP_STATE_REBINDING:
|
||||
|
||||
r = client_handle_ack(client, message, len);
|
||||
if (r >= 0) {
|
||||
client->start_delay = 0;
|
||||
(void) event_source_disable(client->timeout_resend);
|
||||
client->receive_message =
|
||||
sd_event_source_unref(client->receive_message);
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
if (IN_SET(client->state, DHCP_STATE_REQUESTING,
|
||||
DHCP_STATE_REBOOTING))
|
||||
notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
|
||||
else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
|
||||
notify_event = r;
|
||||
|
||||
client->state = DHCP_STATE_BOUND;
|
||||
client->attempt = 0;
|
||||
|
||||
client->last_addr = client->lease->address;
|
||||
|
||||
r = client_set_lease_timeouts(client);
|
||||
if (r < 0) {
|
||||
log_dhcp_client(client, "could not set lease timeouts");
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
|
||||
if (r < 0) {
|
||||
log_dhcp_client(client, "could not bind UDP socket");
|
||||
goto error;
|
||||
}
|
||||
|
||||
client->fd = r;
|
||||
|
||||
client_initialize_io_events(client, client_receive_message_udp);
|
||||
|
||||
if (notify_event) {
|
||||
client_notify(client, notify_event);
|
||||
if (client->state == DHCP_STATE_STOPPED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else if (r == -EADDRNOTAVAIL) {
|
||||
if (r == -ENOMSG)
|
||||
return 0; /* invalid message, let's ignore it */
|
||||
if (r == -EADDRNOTAVAIL) {
|
||||
/* got a NAK, let's restart the client */
|
||||
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
|
||||
|
||||
|
|
@ -1877,39 +1833,75 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX,
|
||||
client->start_delay, USEC_PER_SEC));
|
||||
log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC));
|
||||
|
||||
client->start_delay = CLAMP(client->start_delay * 2,
|
||||
RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
|
||||
|
||||
return 0;
|
||||
} else if (r == -ENOMSG)
|
||||
/* invalid message, let's ignore it */
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (IN_SET(client->state, DHCP_STATE_REQUESTING, DHCP_STATE_REBOOTING))
|
||||
notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
|
||||
else
|
||||
notify_event = r;
|
||||
|
||||
client->start_delay = 0;
|
||||
(void) event_source_disable(client->timeout_resend);
|
||||
client->receive_message = sd_event_source_unref(client->receive_message);
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
client->state = DHCP_STATE_BOUND;
|
||||
client->attempt = 0;
|
||||
|
||||
client->last_addr = client->lease->address;
|
||||
|
||||
r = client_set_lease_timeouts(client);
|
||||
if (r < 0) {
|
||||
log_dhcp_client(client, "could not set lease timeouts");
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
|
||||
if (r < 0) {
|
||||
log_dhcp_client(client, "could not bind UDP socket");
|
||||
goto error;
|
||||
}
|
||||
|
||||
client->fd = r;
|
||||
|
||||
client_initialize_io_events(client, client_receive_message_udp);
|
||||
|
||||
if (IN_SET(client->state, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING) &&
|
||||
notify_event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
|
||||
/* FIXME: hmm, maybe this is a bug... */
|
||||
log_dhcp_client(client, "client_handle_ack() returned SD_DHCP_CLIENT_EVENT_IP_ACQUIRE while DHCP client is %s the address, skipping callback.",
|
||||
client->state == DHCP_STATE_RENEWING ? "renewing" : "rebinding");
|
||||
else
|
||||
client_notify(client, notify_event);
|
||||
break;
|
||||
|
||||
case DHCP_STATE_BOUND:
|
||||
r = client_handle_forcerenew(client, message, len);
|
||||
if (r >= 0) {
|
||||
r = client_timeout_t1(NULL, 0, client);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
} else if (r == -ENOMSG)
|
||||
/* invalid message, let's ignore it */
|
||||
return 0;
|
||||
if (r == -ENOMSG)
|
||||
return 0; /* invalid message, let's ignore it */
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = client_timeout_t1(NULL, 0, client);
|
||||
break;
|
||||
|
||||
case DHCP_STATE_INIT:
|
||||
case DHCP_STATE_INIT_REBOOT:
|
||||
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case DHCP_STATE_STOPPED:
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
default:
|
||||
assert_not_reached("invalid state");
|
||||
}
|
||||
|
||||
error:
|
||||
|
|
|
|||
|
|
@ -956,7 +956,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
|
|||
usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
|
||||
usec_t max_retransmit_duration = 0;
|
||||
uint8_t max_retransmit_count = 0;
|
||||
char time_string[FORMAT_TIMESPAN_MAX];
|
||||
|
||||
assert(s);
|
||||
assert(client);
|
||||
|
|
@ -1052,7 +1051,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
|
|||
}
|
||||
|
||||
log_dhcp6_client(client, "Next retransmission in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
|
||||
FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
clock_boottime_or_monotonic(),
|
||||
|
|
@ -1576,7 +1575,6 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
|
|||
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
||||
int r;
|
||||
usec_t timeout, time_now;
|
||||
char time_string[FORMAT_TIMESPAN_MAX];
|
||||
uint32_t lifetime_t1, lifetime_t2;
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
|
|
@ -1649,8 +1647,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
|
||||
timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
|
||||
|
||||
log_dhcp6_client(client, "T1 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
||||
log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_t1,
|
||||
clock_boottime_or_monotonic(),
|
||||
|
|
@ -1662,8 +1659,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
|
||||
timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
|
||||
|
||||
log_dhcp6_client(client, "T2 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
||||
log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_t2,
|
||||
clock_boottime_or_monotonic(),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "fd-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "log-link.h"
|
||||
#include "memory-util.h"
|
||||
#include "network-common.h"
|
||||
#include "random-util.h"
|
||||
#include "siphash24.h"
|
||||
|
|
@ -66,7 +67,7 @@ struct sd_ipv4acd {
|
|||
sd_event_source *timer_event_source;
|
||||
|
||||
usec_t defend_window;
|
||||
be32_t address;
|
||||
struct in_addr address;
|
||||
|
||||
/* External */
|
||||
struct ether_addr mac_addr;
|
||||
|
|
@ -74,7 +75,9 @@ struct sd_ipv4acd {
|
|||
sd_event *event;
|
||||
int event_priority;
|
||||
sd_ipv4acd_callback_t callback;
|
||||
void* userdata;
|
||||
void *userdata;
|
||||
sd_ipv4acd_check_mac_callback_t check_mac_callback;
|
||||
void *check_mac_userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4acd_errno(acd, error, fmt, ...) \
|
||||
|
|
@ -210,18 +213,6 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u
|
|||
acd->event_priority, "ipv4acd-timer", true);
|
||||
}
|
||||
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
|
||||
assert(acd);
|
||||
assert(arp);
|
||||
|
||||
/* see the BPF */
|
||||
if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
|
||||
return true;
|
||||
|
||||
/* the TPA matched instead of the SPA, this is not a conflict */
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_ipv4acd *acd = userdata;
|
||||
int r = 0;
|
||||
|
|
@ -231,38 +222,32 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
|
|||
switch (acd->state) {
|
||||
|
||||
case IPV4ACD_STATE_STARTED:
|
||||
acd->defend_window = 0;
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
|
||||
|
||||
if (acd->n_conflict >= MAX_CONFLICTS) {
|
||||
char ts[FORMAT_TIMESPAN_MAX];
|
||||
log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
|
||||
|
||||
log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
|
||||
FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0));
|
||||
r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
} else {
|
||||
} else
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
/* Send a probe */
|
||||
r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
|
||||
r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
|
||||
goto fail;
|
||||
} else {
|
||||
_cleanup_free_ char *address = NULL;
|
||||
union in_addr_union addr = { .in.s_addr = acd->address };
|
||||
|
||||
(void) in_addr_to_string(AF_INET, &addr, &address);
|
||||
log_ipv4acd(acd, "Probing %s", strna(address));
|
||||
}
|
||||
|
||||
log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
|
||||
|
||||
if (acd->n_iteration < PROBE_NUM - 2) {
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
|
||||
|
||||
|
|
@ -288,12 +273,13 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
|
|||
_fallthrough_;
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
/* Send announcement packet */
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
|
||||
goto fail;
|
||||
} else
|
||||
log_ipv4acd(acd, "ANNOUNCE");
|
||||
}
|
||||
|
||||
log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
|
||||
|
||||
|
|
@ -319,16 +305,45 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
|
||||
_cleanup_free_ char *address = NULL;
|
||||
union in_addr_union addr = { .in.s_addr = acd->address };
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
|
||||
assert(acd);
|
||||
assert(arp);
|
||||
|
||||
/* RFC 5227 section 2.1.1.
|
||||
* "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
|
||||
* being performed, where the packet's 'sender IP address' is the address being probed for,
|
||||
* then the host MUST treat this address as being in use by some other host" */
|
||||
if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
|
||||
return true;
|
||||
|
||||
if (announced)
|
||||
/* the TPA matched instead of SPA, this is not a conflict */
|
||||
return false;
|
||||
|
||||
/* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
|
||||
* the packet's 'sender hardware address' is not the hardware address of any of the host's
|
||||
* interfaces, then the host SHOULD similarly treat this as an address conflict" */
|
||||
if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
|
||||
return false; /* not ARP Request, ignoring. */
|
||||
if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
|
||||
return false; /* not ARP Probe, ignoring. */
|
||||
if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
|
||||
return false; /* target IP address does not match, BPF code is broken? */
|
||||
|
||||
if (acd->check_mac_callback &&
|
||||
acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
|
||||
/* sender hardware is one of the host's interfaces, ignoring. */
|
||||
return true;
|
||||
|
||||
return true; /* conflict! */
|
||||
}
|
||||
|
||||
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
acd->n_conflict++;
|
||||
|
||||
(void) in_addr_to_string(AF_INET, &addr, &address);
|
||||
log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
|
||||
log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
|
||||
|
||||
ipv4acd_reset(acd);
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
|
||||
|
|
@ -367,7 +382,7 @@ static int ipv4acd_on_packet(
|
|||
case IPV4ACD_STATE_ANNOUNCING:
|
||||
case IPV4ACD_STATE_RUNNING:
|
||||
|
||||
if (ipv4acd_arp_conflict(acd, &packet)) {
|
||||
if (ipv4acd_arp_conflict(acd, &packet, true)) {
|
||||
usec_t ts;
|
||||
|
||||
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
|
|
@ -375,12 +390,13 @@ static int ipv4acd_on_packet(
|
|||
/* Defend address */
|
||||
if (ts > acd->defend_window) {
|
||||
acd->defend_window = ts + DEFEND_INTERVAL_USEC;
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
|
||||
goto fail;
|
||||
} else
|
||||
log_ipv4acd(acd, "DEFEND");
|
||||
}
|
||||
|
||||
log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
|
||||
|
||||
} else
|
||||
ipv4acd_on_conflict(acd);
|
||||
|
|
@ -390,8 +406,8 @@ static int ipv4acd_on_packet(
|
|||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
/* BPF ensures this packet indicates a conflict */
|
||||
ipv4acd_on_conflict(acd);
|
||||
if (ipv4acd_arp_conflict(acd, &packet, false))
|
||||
ipv4acd_on_conflict(acd);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -440,12 +456,24 @@ const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
|
|||
}
|
||||
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
assert_return(!ether_addr_is_null(addr), -EINVAL);
|
||||
|
||||
acd->mac_addr = *addr;
|
||||
|
||||
if (!sd_ipv4acd_is_running(acd))
|
||||
return 0;
|
||||
|
||||
assert(acd->fd >= 0);
|
||||
r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
ipv4acd_reset(acd);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -485,21 +513,51 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
acd->check_mac_callback = cb;
|
||||
acd->check_mac_userdata = userdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
assert_return(in4_addr_is_set(address), -EINVAL);
|
||||
|
||||
acd->address = address->s_addr;
|
||||
if (in4_addr_equal(&acd->address, address))
|
||||
return 0;
|
||||
|
||||
acd->address = *address;
|
||||
|
||||
if (!sd_ipv4acd_is_running(acd))
|
||||
return 0;
|
||||
|
||||
assert(acd->fd >= 0);
|
||||
r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, 0);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ipv4acd_reset(acd);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
|
||||
address->s_addr = acd->address;
|
||||
*address = acd->address;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -516,16 +574,15 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
|
|||
assert_return(acd, -EINVAL);
|
||||
assert_return(acd->event, -EINVAL);
|
||||
assert_return(acd->ifindex > 0, -EINVAL);
|
||||
assert_return(acd->address != 0, -EINVAL);
|
||||
assert_return(in4_addr_is_set(&acd->address), -EINVAL);
|
||||
assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
|
||||
r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
CLOSE_AND_REPLACE(acd->fd, r);
|
||||
acd->defend_window = 0;
|
||||
|
||||
if (reset_conflicts)
|
||||
acd->n_conflict = 0;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ struct sd_ipv4ll {
|
|||
be32_t claimed_address;
|
||||
|
||||
sd_ipv4ll_callback_t callback;
|
||||
void* userdata;
|
||||
void *userdata;
|
||||
|
||||
sd_ipv4ll_check_mac_callback_t check_mac_callback;
|
||||
void *check_mac_userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4ll_errno(ll, error, fmt, ...) \
|
||||
|
|
@ -62,7 +65,8 @@ struct sd_ipv4ll {
|
|||
sd_ipv4ll_get_ifname(ll), \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
|
||||
static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
|
||||
static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
|
||||
|
||||
static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
|
||||
assert(ll);
|
||||
|
|
@ -93,6 +97,10 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(ll);
|
||||
|
||||
return 0;
|
||||
|
|
@ -139,7 +147,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
|
|||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
|
||||
assert_return(!ether_addr_is_null(addr), -EINVAL);
|
||||
|
||||
r = sd_ipv4acd_set_mac(ll->acd, addr);
|
||||
if (r < 0)
|
||||
|
|
@ -170,6 +178,15 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdat
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
ll->check_mac_callback = cb;
|
||||
ll->check_mac_userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
|
|
@ -353,3 +370,14 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
|
|||
error:
|
||||
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
|
||||
}
|
||||
|
||||
static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
|
||||
sd_ipv4ll *ll = userdata;
|
||||
|
||||
assert(ll);
|
||||
|
||||
if (ll->check_mac_callback)
|
||||
return ll->check_mac_callback(ll, mac, ll->check_mac_userdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
|
|||
SOURCE_DEFER, \
|
||||
SOURCE_INOTIFY)
|
||||
|
||||
/* This is used to assert that we didn't pass an unexpected source type to event_source_time_prioq_put().
|
||||
* Time sources and ratelimited sources can be passed, so effectively this is the same as the
|
||||
* EVENT_SOURCE_CAN_RATE_LIMIT() macro. */
|
||||
#define EVENT_SOURCE_USES_TIME_PRIOQ(t) EVENT_SOURCE_CAN_RATE_LIMIT(t)
|
||||
|
||||
struct sd_event {
|
||||
unsigned n_ref;
|
||||
|
||||
|
|
@ -1200,6 +1205,7 @@ static int event_source_time_prioq_put(
|
|||
|
||||
assert(s);
|
||||
assert(d);
|
||||
assert(EVENT_SOURCE_USES_TIME_PRIOQ(s->type));
|
||||
|
||||
r = prioq_put(d->earliest, s, &s->earliest_index);
|
||||
if (r < 0)
|
||||
|
|
@ -2978,6 +2984,7 @@ static int event_arm_timer(
|
|||
d->needs_rearm = false;
|
||||
|
||||
a = prioq_peek(d->earliest);
|
||||
assert(!a || EVENT_SOURCE_USES_TIME_PRIOQ(a->type));
|
||||
if (!a || a->enabled == SD_EVENT_OFF || time_event_source_next(a) == USEC_INFINITY) {
|
||||
|
||||
if (d->fd < 0)
|
||||
|
|
@ -2995,7 +3002,8 @@ static int event_arm_timer(
|
|||
}
|
||||
|
||||
b = prioq_peek(d->latest);
|
||||
assert_se(b && b->enabled != SD_EVENT_OFF);
|
||||
assert(!b || EVENT_SOURCE_USES_TIME_PRIOQ(b->type));
|
||||
assert(b && b->enabled != SD_EVENT_OFF);
|
||||
|
||||
t = sleep_between(e, time_event_source_next(a), time_event_source_latest(b));
|
||||
if (d->next == t)
|
||||
|
|
@ -3075,6 +3083,8 @@ static int process_timer(
|
|||
|
||||
for (;;) {
|
||||
s = prioq_peek(d->earliest);
|
||||
assert(!s || EVENT_SOURCE_USES_TIME_PRIOQ(s->type));
|
||||
|
||||
if (!s || time_event_source_next(s) > n)
|
||||
break;
|
||||
|
||||
|
|
@ -3635,6 +3645,8 @@ static int dispatch_exit(sd_event *e) {
|
|||
assert(e);
|
||||
|
||||
p = prioq_peek(e->exit);
|
||||
assert(!p || p->type == SOURCE_EXIT);
|
||||
|
||||
if (!p || event_source_is_offline(p)) {
|
||||
e->state = SD_EVENT_FINISHED;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
|
|
@ -13,7 +18,9 @@
|
|||
#include "hexdecoct.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#if !HAVE_OPENSSL
|
||||
#include "khash.h"
|
||||
#endif
|
||||
#include "macro.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "random-util.h"
|
||||
|
|
@ -276,13 +283,28 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
|
|||
}
|
||||
|
||||
static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
|
||||
_cleanup_(khash_unrefp) khash *h = NULL;
|
||||
sd_id128_t result;
|
||||
const void *p;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
/* We prefer doing this in-process, since we this means we are not dependent on kernel configuration,
|
||||
* and this also works in locked down container environments. But some distros don't like OpenSSL's
|
||||
* license and its (in-) compatibility with GPL2, hence also support khash */
|
||||
uint8_t md[256/8];
|
||||
if (!HMAC(EVP_sha256(),
|
||||
&base, sizeof(base),
|
||||
(const unsigned char*) &app_id, sizeof(app_id),
|
||||
md, NULL))
|
||||
return -ENOTRECOVERABLE;
|
||||
|
||||
/* Take only the first half. */
|
||||
memcpy(&result, md, MIN(sizeof(md), sizeof(result)));
|
||||
#else
|
||||
_cleanup_(khash_unrefp) khash *h = NULL;
|
||||
const void *p;
|
||||
int r;
|
||||
|
||||
r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
@ -297,6 +319,7 @@ static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret)
|
|||
|
||||
/* We chop off the trailing 16 bytes */
|
||||
memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
|
||||
#endif
|
||||
|
||||
*ret = id128_make_v4_uuid(result);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ enum {
|
|||
|
||||
typedef struct sd_ipv4acd sd_ipv4acd;
|
||||
typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata);
|
||||
typedef int (*sd_ipv4acd_check_mac_callback_t)(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
|
||||
|
||||
int sd_ipv4acd_detach_event(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority);
|
||||
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address);
|
||||
int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata);
|
||||
int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata);
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
|
||||
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
|
||||
int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd);
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ enum {
|
|||
|
||||
typedef struct sd_ipv4ll sd_ipv4ll;
|
||||
typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata);
|
||||
typedef int (*sd_ipv4ll_check_mac_callback_t)(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata);
|
||||
|
||||
int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
|
||||
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
|
||||
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
|
||||
int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata);
|
||||
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
|
||||
int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
|
||||
int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "escape.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
int cescape_char(char c, char *buf) {
|
||||
|
|
@ -291,10 +292,12 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
|
||||
char *r, *t;
|
||||
ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
|
||||
_cleanup_free_ char *ans = NULL;
|
||||
char *t;
|
||||
const char *f;
|
||||
size_t pl;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
|
|
@ -303,18 +306,17 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
|
|||
|
||||
pl = strlen_ptr(prefix);
|
||||
|
||||
r = new(char, pl+length+1);
|
||||
if (!r)
|
||||
ans = new(char, pl+length+1);
|
||||
if (!ans)
|
||||
return -ENOMEM;
|
||||
|
||||
if (prefix)
|
||||
memcpy(r, prefix, pl);
|
||||
memcpy(ans, prefix, pl);
|
||||
|
||||
for (f = s, t = r + pl; f < s + length; f++) {
|
||||
for (f = s, t = ans + pl; f < s + length; f++) {
|
||||
size_t remaining;
|
||||
bool eight_bit = false;
|
||||
char32_t u;
|
||||
int k;
|
||||
|
||||
remaining = s + length - f;
|
||||
assert(remaining > 0);
|
||||
|
|
@ -332,23 +334,21 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
|
|||
continue;
|
||||
}
|
||||
|
||||
free(r);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
|
||||
if (k < 0) {
|
||||
r = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
|
||||
if (r < 0) {
|
||||
if (flags & UNESCAPE_RELAX) {
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
*(t++) = '\\';
|
||||
continue;
|
||||
}
|
||||
|
||||
free(r);
|
||||
return k;
|
||||
return r;
|
||||
}
|
||||
|
||||
f += k;
|
||||
f += r;
|
||||
if (eight_bit)
|
||||
/* One byte? Set directly as specified */
|
||||
*(t++) = u;
|
||||
|
|
@ -359,8 +359,9 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
|
|||
|
||||
*t = 0;
|
||||
|
||||
*ret = r;
|
||||
return t - r;
|
||||
assert(t >= ans); /* Let static analyzers know that the answer is non-negative. */
|
||||
*ret = TAKE_PTR(ans);
|
||||
return t - *ret;
|
||||
}
|
||||
|
||||
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
|
||||
|
|
@ -445,7 +446,6 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
|
|||
console_width,
|
||||
FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char* octescape(const char *s, size_t len) {
|
||||
char *r, *t;
|
||||
|
|
@ -546,3 +546,24 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
|
|||
|
||||
return str_realloc(buf);
|
||||
}
|
||||
|
||||
char* quote_command_line(char **argv) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
|
||||
assert(argv);
|
||||
|
||||
char **a;
|
||||
STRV_FOREACH(a, argv) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY);
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
if (!strextend_with_separator(&result, " ", t))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return TAKE_PTR(result);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -45,14 +45,15 @@ char* cescape(const char *s);
|
|||
char* cescape_length(const char *s, size_t n);
|
||||
int cescape_char(char c, char *buf);
|
||||
|
||||
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
|
||||
static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
|
||||
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
|
||||
|
||||
ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
|
||||
static inline ssize_t cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
|
||||
return cunescape_length_with_prefix(s, length, NULL, flags, ret);
|
||||
}
|
||||
static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
|
||||
static inline ssize_t cunescape(const char *s, UnescapeFlags flags, char **ret) {
|
||||
return cunescape_length(s, strlen(s), flags, ret);
|
||||
}
|
||||
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
|
||||
|
||||
typedef enum XEscapeFlags {
|
||||
XESCAPE_8_BIT = 1 << 0,
|
||||
|
|
@ -68,3 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
|
|||
|
||||
char* shell_escape(const char *s, const char *bad);
|
||||
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
|
||||
char* quote_command_line(char **argv);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,16 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b)
|
|||
return memcmp(a->bytes, b->bytes, a->length);
|
||||
}
|
||||
|
||||
static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
|
||||
assert(p);
|
||||
assert(state);
|
||||
|
||||
siphash24_compress(&p->length, sizeof(p->length), state);
|
||||
siphash24_compress(p->bytes, p->length, state);
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
|
||||
|
||||
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
|
||||
assert(addr);
|
||||
assert(buffer);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ struct hw_addr_data {
|
|||
#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
|
||||
char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
|
||||
|
||||
/* Use only as function argument, never stand-alone! */
|
||||
/* Note: the lifetime of the compound literal is the immediately surrounding block,
|
||||
* see C11 §6.5.2.5, and
|
||||
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
|
||||
#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){})
|
||||
|
||||
#define HW_ADDR_NULL ((const struct hw_addr_data){})
|
||||
|
|
@ -36,6 +38,8 @@ static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
|
|||
return hw_addr_equal(addr, &HW_ADDR_NULL);
|
||||
}
|
||||
|
||||
extern const struct hash_ops hw_addr_hash_ops;
|
||||
|
||||
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
|
||||
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
assert(!FLAGS_SET(flags, EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE));
|
||||
|
||||
/* Bail early if called after last value or with no input */
|
||||
if (!*p)
|
||||
|
|
@ -52,7 +53,8 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
goto finish_force_terminate;
|
||||
else if (strchr(separators, c)) {
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
|
||||
(*p)++;
|
||||
if (!(flags & EXTRACT_RETAIN_SEPARATORS))
|
||||
(*p)++;
|
||||
goto finish_force_next;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -125,48 +127,58 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
return -EINVAL;
|
||||
} else if (c == quote) { /* found the end quote */
|
||||
quote = 0;
|
||||
break;
|
||||
if (flags & EXTRACT_UNQUOTE)
|
||||
break;
|
||||
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
|
||||
backslash = true;
|
||||
break;
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(s, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
s[sz++] = c;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(s, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
s[sz++] = c;
|
||||
|
||||
if (quote == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
for (;; (*p)++, c = **p) {
|
||||
if (c == 0)
|
||||
goto finish_force_terminate;
|
||||
else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
|
||||
else if (IN_SET(c, '\'', '"') && (flags & (EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE))) {
|
||||
quote = c;
|
||||
break;
|
||||
if (flags & EXTRACT_UNQUOTE)
|
||||
break;
|
||||
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
|
||||
backslash = true;
|
||||
break;
|
||||
} else if (strchr(separators, c)) {
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
|
||||
(*p)++;
|
||||
if (!(flags & EXTRACT_RETAIN_SEPARATORS))
|
||||
(*p)++;
|
||||
goto finish_force_next;
|
||||
}
|
||||
/* Skip additional coalesced separators. */
|
||||
for (;; (*p)++, c = **p) {
|
||||
if (c == 0)
|
||||
goto finish_force_terminate;
|
||||
if (!strchr(separators, c))
|
||||
break;
|
||||
}
|
||||
if (!(flags & EXTRACT_RETAIN_SEPARATORS))
|
||||
/* Skip additional coalesced separators. */
|
||||
for (;; (*p)++, c = **p) {
|
||||
if (c == 0)
|
||||
goto finish_force_terminate;
|
||||
if (!strchr(separators, c))
|
||||
break;
|
||||
}
|
||||
goto finish;
|
||||
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(s, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
s[sz++] = c;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(s, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
s[sz++] = c;
|
||||
|
||||
if (quote != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ typedef enum ExtractFlags {
|
|||
EXTRACT_CUNESCAPE = 1 << 1, /* Unescape known escape sequences. */
|
||||
EXTRACT_UNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
|
||||
EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */
|
||||
EXTRACT_UNQUOTE = 1 << 4, /* Remove quoting with "" and ''. */
|
||||
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */
|
||||
EXTRACT_RETAIN_ESCAPE = 1 << 6, /* Treat escape character '\' as any other character without special meaning */
|
||||
EXTRACT_KEEP_QUOTE = 1 << 4, /* Ignore separators in quoting with "" and ''. */
|
||||
EXTRACT_UNQUOTE = 1 << 5, /* Ignore separators in quoting with "" and '', and remove the quotes. */
|
||||
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 6, /* Don't treat multiple adjacent separators as one */
|
||||
EXTRACT_RETAIN_ESCAPE = 1 << 7, /* Treat escape character '\' as any other character without special meaning */
|
||||
EXTRACT_RETAIN_SEPARATORS = 1 << 8, /* Do not advance the original string pointer past the separator(s) */
|
||||
|
||||
/* Note that if no flags are specified, escaped escape characters will be silently stripped. */
|
||||
} ExtractFlags;
|
||||
|
|
|
|||
|
|
@ -4,19 +4,20 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/btrfs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "copy.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "macro.h"
|
||||
#include "memfd-util.h"
|
||||
#include "missing_fcntl.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "parse-util.h"
|
||||
|
|
@ -210,10 +211,9 @@ static int get_max_fd(void) {
|
|||
return (int) (m - 1);
|
||||
}
|
||||
|
||||
int close_all_fds(const int except[], size_t n_except) {
|
||||
int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
|
||||
static bool have_close_range = true; /* Assume we live in the future */
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
int r = 0;
|
||||
|
||||
assert(n_except == 0 || except);
|
||||
|
|
@ -229,129 +229,104 @@ int close_all_fds(const int except[], size_t n_except) {
|
|||
/* Close everything. Yay! */
|
||||
|
||||
if (close_range(3, -1, 0) >= 0)
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
|
||||
have_close_range = false;
|
||||
else
|
||||
return -errno;
|
||||
|
||||
have_close_range = false;
|
||||
} else {
|
||||
_cleanup_free_ int *sorted_malloc = NULL;
|
||||
size_t n_sorted;
|
||||
int *sorted;
|
||||
typesafe_qsort(except, n_except, cmp_int);
|
||||
|
||||
assert(n_except < SIZE_MAX);
|
||||
n_sorted = n_except + 1;
|
||||
for (size_t i = 0; i < n_except; i++) {
|
||||
int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */
|
||||
int end = MAX(except[i], 2);
|
||||
|
||||
if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
|
||||
sorted = sorted_malloc = new(int, n_sorted);
|
||||
else
|
||||
sorted = newa(int, n_sorted);
|
||||
assert(end >= start);
|
||||
|
||||
if (sorted) {
|
||||
int c = 0;
|
||||
|
||||
memcpy(sorted, except, n_except * sizeof(int));
|
||||
|
||||
/* Let's add fd 2 to the list of fds, to simplify the loop below, as this
|
||||
* allows us to cover the head of the array the same way as the body */
|
||||
sorted[n_sorted-1] = 2;
|
||||
|
||||
typesafe_qsort(sorted, n_sorted, cmp_int);
|
||||
|
||||
for (size_t i = 0; i < n_sorted-1; i++) {
|
||||
int start, end;
|
||||
|
||||
start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
|
||||
end = MAX(sorted[i+1], 2);
|
||||
|
||||
assert(end >= start);
|
||||
|
||||
if (end - start <= 1)
|
||||
continue;
|
||||
|
||||
/* Close everything between the start and end fds (both of which shall stay open) */
|
||||
if (close_range(start + 1, end - 1, 0) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
if (end - start <= 1)
|
||||
continue;
|
||||
|
||||
/* Close everything between the start and end fds (both of which shall stay open) */
|
||||
if (close_range(start + 1, end - 1, 0) < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
|
||||
have_close_range = false;
|
||||
break;
|
||||
}
|
||||
|
||||
c += end - start - 1;
|
||||
}
|
||||
|
||||
if (have_close_range) {
|
||||
/* The loop succeeded. Let's now close everything beyond the end */
|
||||
|
||||
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
|
||||
return c;
|
||||
|
||||
if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
|
||||
return c + 1;
|
||||
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
else
|
||||
return -errno;
|
||||
|
||||
have_close_range = false;
|
||||
goto opendir_fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback on OOM or if close_range() is not supported */
|
||||
/* The loop succeeded. Let's now close everything beyond the end */
|
||||
|
||||
if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */
|
||||
return 0;
|
||||
|
||||
int start = MAX(except[n_except-1], 2);
|
||||
|
||||
if (close_range(start + 1, -1, 0) >= 0)
|
||||
return 0;
|
||||
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
|
||||
have_close_range = false;
|
||||
else
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
d = opendir("/proc/self/fd");
|
||||
if (!d) {
|
||||
int fd, max_fd;
|
||||
/* Fallback for when close_range() is not supported */
|
||||
opendir_fallback:
|
||||
d = allow_alloc ? opendir("/proc/self/fd") : NULL;
|
||||
if (d) {
|
||||
struct dirent *de;
|
||||
|
||||
/* When /proc isn't available (for example in chroots) the fallback is brute forcing through
|
||||
* the fd table */
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
int fd = -1, q;
|
||||
|
||||
max_fd = get_max_fd();
|
||||
if (max_fd < 0)
|
||||
return max_fd;
|
||||
if (safe_atoi(de->d_name, &fd) < 0)
|
||||
/* Let's better ignore this, just in case */
|
||||
continue;
|
||||
|
||||
/* Refuse to do the loop over more too many elements. It's better to fail immediately than to
|
||||
* spin the CPU for a long time. */
|
||||
if (max_fd > MAX_FD_LOOP_LIMIT)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
|
||||
"/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
|
||||
max_fd);
|
||||
if (fd < 3)
|
||||
continue;
|
||||
|
||||
for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
|
||||
int q;
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
if (fd_in_set(fd, except, n_except))
|
||||
continue;
|
||||
|
||||
q = close_nointr(fd);
|
||||
if (q < 0 && q != -EBADF && r >= 0)
|
||||
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
|
||||
r = q;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
int fd = -1, q;
|
||||
/* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by
|
||||
* brute-forcing through the file descriptor table. */
|
||||
|
||||
if (safe_atoi(de->d_name, &fd) < 0)
|
||||
/* Let's better ignore this, just in case */
|
||||
continue;
|
||||
int max_fd = get_max_fd();
|
||||
if (max_fd < 0)
|
||||
return max_fd;
|
||||
|
||||
if (fd < 3)
|
||||
continue;
|
||||
/* Refuse to do the loop over more too many elements. It's better to fail immediately than to
|
||||
* spin the CPU for a long time. */
|
||||
if (max_fd > MAX_FD_LOOP_LIMIT)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
|
||||
"/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
|
||||
max_fd);
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
|
||||
int q;
|
||||
|
||||
if (fd_in_set(fd, except, n_except))
|
||||
continue;
|
||||
|
||||
q = close_nointr(fd);
|
||||
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
|
||||
if (q < 0 && q != -EBADF && r >= 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
|
|
@ -524,343 +499,6 @@ int move_fd(int from, int to, int cloexec) {
|
|||
|
||||
return to;
|
||||
}
|
||||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags) {
|
||||
|
||||
_cleanup_close_pair_ int pipefds[2] = { -1, -1 };
|
||||
char pattern[] = "/dev/shm/data-fd-XXXXXX";
|
||||
_cleanup_close_ int fd = -1;
|
||||
int isz = 0, r;
|
||||
ssize_t n;
|
||||
off_t f;
|
||||
|
||||
assert(data || size == 0);
|
||||
|
||||
/* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
|
||||
* complex than I wish it was. But here's why:
|
||||
*
|
||||
* a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
|
||||
* read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
|
||||
*
|
||||
* b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
|
||||
* a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
|
||||
* clients can only bump their size to a system-wide limit, which might be quite low.
|
||||
*
|
||||
* c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
|
||||
* earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
|
||||
* /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
|
||||
*
|
||||
* d) Finally, we try creating a regular file in /dev/shm, which we then delete.
|
||||
*
|
||||
* It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
|
||||
* figure. */
|
||||
|
||||
if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
|
||||
/* As a special case, return /dev/null if we have been called for an empty data block */
|
||||
r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((flags & ACQUIRE_NO_MEMFD) == 0) {
|
||||
fd = memfd_new("data-fd");
|
||||
if (fd < 0)
|
||||
goto try_pipe;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
f = lseek(fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
r = memfd_set_sealed(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
try_pipe:
|
||||
if ((flags & ACQUIRE_NO_PIPE) == 0) {
|
||||
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||
return -errno;
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
if ((size_t) isz < size) {
|
||||
isz = (int) size;
|
||||
if (isz < 0 || (size_t) isz != size)
|
||||
return -E2BIG;
|
||||
|
||||
/* Try to bump the pipe size */
|
||||
(void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
|
||||
|
||||
/* See if that worked */
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
if ((size_t) isz < size)
|
||||
goto try_dev_shm;
|
||||
}
|
||||
|
||||
n = write(pipefds[1], data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
(void) fd_nonblock(pipefds[0], false);
|
||||
|
||||
return TAKE_FD(pipefds[0]);
|
||||
}
|
||||
|
||||
try_dev_shm:
|
||||
if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
|
||||
fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
|
||||
if (fd < 0)
|
||||
goto try_dev_shm_without_o_tmpfile;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
|
||||
}
|
||||
|
||||
try_dev_shm_without_o_tmpfile:
|
||||
if ((flags & ACQUIRE_NO_REGULAR) == 0) {
|
||||
fd = mkostemp_safe(pattern);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0) {
|
||||
r = -errno;
|
||||
goto unlink_and_return;
|
||||
}
|
||||
if ((size_t) n != size) {
|
||||
r = -EIO;
|
||||
goto unlink_and_return;
|
||||
}
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
r = open(pattern, O_RDONLY|O_CLOEXEC);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
|
||||
unlink_and_return:
|
||||
(void) unlink(pattern);
|
||||
return r;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
|
||||
#define DATA_FD_MEMORY_LIMIT (64U*1024U)
|
||||
|
||||
/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
|
||||
#define DATA_FD_TMP_LIMIT (1024U*1024U)
|
||||
|
||||
int fd_duplicate_data_fd(int fd) {
|
||||
|
||||
_cleanup_close_ int copy_fd = -1, tmp_fd = -1;
|
||||
_cleanup_free_ void *remains = NULL;
|
||||
size_t remains_size = 0;
|
||||
const char *td;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
/* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
|
||||
* independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
|
||||
* somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
|
||||
* uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
|
||||
* /var/tmp. */
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
/* For now, let's only accept regular files, sockets, pipes and char devices */
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return -EISDIR;
|
||||
if (S_ISLNK(st.st_mode))
|
||||
return -ELOOP;
|
||||
if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
|
||||
return -EBADFD;
|
||||
|
||||
/* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
|
||||
* that we use the reported regular file size only as a hint, given that there are plenty special files in
|
||||
* /proc and /sys which report a zero file size but can be read from. */
|
||||
|
||||
if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
/* Try a memfd first */
|
||||
copy_fd = memfd_new("data-fd");
|
||||
if (copy_fd >= 0) {
|
||||
off_t f;
|
||||
|
||||
r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
f = lseek(copy_fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
if (r == 0) {
|
||||
/* Did it fit into the limit? If so, we are done. */
|
||||
r = memfd_set_sealed(copy_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return TAKE_FD(copy_fd);
|
||||
}
|
||||
|
||||
/* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
|
||||
|
||||
} else {
|
||||
_cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
|
||||
int isz;
|
||||
|
||||
/* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
|
||||
* then block indefinitely when we hit the pipe size limit */
|
||||
|
||||
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||
return -errno;
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
/* Try to enlarge the pipe size if necessary */
|
||||
if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
(void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
|
||||
if (r < 0 && r != -EAGAIN)
|
||||
return r; /* If we get EAGAIN it could be because of the source or because of
|
||||
* the destination fd, we can't know, as sendfile() and friends won't
|
||||
* tell us. Hence, treat this as reason to fall back, just to be
|
||||
* sure. */
|
||||
if (r == 0) {
|
||||
/* Everything fit in, yay! */
|
||||
(void) fd_nonblock(pipefds[0], false);
|
||||
|
||||
return TAKE_FD(pipefds[0]);
|
||||
}
|
||||
|
||||
/* Things didn't fit in. But we read data into the pipe, let's remember that, so that
|
||||
* when writing the new file we incorporate this first. */
|
||||
copy_fd = TAKE_FD(pipefds[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
|
||||
if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
|
||||
(DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
|
||||
off_t f;
|
||||
|
||||
tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
|
||||
if (tmp_fd < 0)
|
||||
return tmp_fd;
|
||||
|
||||
if (copy_fd >= 0) {
|
||||
/* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
|
||||
* temporary file first. */
|
||||
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
if (remains_size > 0) {
|
||||
/* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
|
||||
* failed copy operation, let's flush them out next. */
|
||||
|
||||
r = loop_write(tmp_fd, remains, remains_size, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
goto finish; /* Yay, it fit in */
|
||||
|
||||
/* It didn't fit in. Let's not forget to use what we already used */
|
||||
f = lseek(tmp_fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
CLOSE_AND_REPLACE(copy_fd, tmp_fd);
|
||||
|
||||
remains = mfree(remains);
|
||||
remains_size = 0;
|
||||
}
|
||||
|
||||
/* As last fallback use /var/tmp */
|
||||
r = var_tmp_dir(&td);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
|
||||
if (tmp_fd < 0)
|
||||
return tmp_fd;
|
||||
|
||||
if (copy_fd >= 0) {
|
||||
/* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
|
||||
* into the temporary file first. */
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
if (remains_size > 0) {
|
||||
/* Then, copy in any read but not yet written bytes. */
|
||||
r = loop_write(tmp_fd, remains, remains_size, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Copy in the rest */
|
||||
r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
|
||||
finish:
|
||||
/* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
|
||||
* file again */
|
||||
|
||||
return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int fd_move_above_stdio(int fd) {
|
||||
|
|
@ -1064,4 +702,21 @@ int read_nr_open(void) {
|
|||
/* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */
|
||||
return 1024 * 1024;
|
||||
}
|
||||
|
||||
/* This is here because it's fd-related and is called from sd-journal code. Other btrfs-related utilities are
|
||||
* in src/shared, but libsystemd must not link to libsystemd-shared, see docs/ARCHITECTURE.md. */
|
||||
int btrfs_defrag_fd(int fd) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = fd_verify_regular(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -56,7 +56,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
|
|||
int fd_nonblock(int fd, bool nonblock);
|
||||
int fd_cloexec(int fd, bool cloexec);
|
||||
|
||||
int close_all_fds(const int except[], size_t n_except);
|
||||
int close_all_fds_full(int except[], size_t n_except, bool allow_alloc);
|
||||
static inline int close_all_fds(int except[], size_t n_except) {
|
||||
return close_all_fds_full(except, n_except, true);
|
||||
}
|
||||
|
||||
int same_fd(int a, int b);
|
||||
|
||||
|
|
@ -76,10 +79,6 @@ enum {
|
|||
ACQUIRE_NO_REGULAR = 1 << 4,
|
||||
};
|
||||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags);
|
||||
|
||||
int fd_duplicate_data_fd(int fd);
|
||||
|
||||
int fd_move_above_stdio(int fd);
|
||||
|
||||
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
|
||||
|
|
@ -107,5 +106,5 @@ static inline int make_null_stdio(void) {
|
|||
|
||||
|
||||
int fd_reopen(int fd, int flags);
|
||||
|
||||
int read_nr_open(void);
|
||||
int btrfs_defrag_fd(int fd);
|
||||
|
|
|
|||
|
|
@ -526,18 +526,17 @@ int read_full_stream_full(
|
|||
size_t *ret_size) {
|
||||
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t n, n_next, l;
|
||||
size_t n, n_next = 0, l;
|
||||
int fd, r;
|
||||
|
||||
assert(f);
|
||||
assert(ret_contents);
|
||||
assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
|
||||
assert(size != SIZE_MAX || !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER));
|
||||
|
||||
if (offset != UINT64_MAX && offset > LONG_MAX)
|
||||
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
|
||||
return -ERANGE;
|
||||
|
||||
n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */
|
||||
|
||||
fd = fileno(f);
|
||||
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
|
||||
* fmemopen()), let's optimize our buffering */
|
||||
|
|
@ -547,20 +546,20 @@ int read_full_stream_full(
|
|||
return -errno;
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
if (size == SIZE_MAX) {
|
||||
|
||||
/* Try to start with the right file size if we shall read the file in full. Note
|
||||
* that we increase the size to read here by one, so that the first read attempt
|
||||
* already makes us notice the EOF. If the reported size of the file is zero, we
|
||||
* avoid this logic however, since quite likely it might be a virtual file in procfs
|
||||
* that all report a zero file size. */
|
||||
|
||||
if (st.st_size > 0 &&
|
||||
(size == SIZE_MAX || FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER))) {
|
||||
|
||||
uint64_t rsize =
|
||||
LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset);
|
||||
|
||||
/* Safety check */
|
||||
if (rsize > READ_FULL_BYTES_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
/* Start with the right file size. Note that we increase the size to read
|
||||
* here by one, so that the first read attempt already makes us notice the
|
||||
* EOF. If the reported size of the file is zero, we avoid this logic
|
||||
* however, since quite likely it might be a virtual file in procfs that all
|
||||
* report a zero file size. */
|
||||
if (st.st_size > 0)
|
||||
if (rsize < SIZE_MAX) /* overflow check */
|
||||
n_next = rsize + 1;
|
||||
}
|
||||
|
||||
|
|
@ -569,6 +568,17 @@ int read_full_stream_full(
|
|||
}
|
||||
}
|
||||
|
||||
/* If we don't know how much to read, figure it out now. If we shall read a part of the file, then
|
||||
* allocate the requested size. If we shall load the full file start with LINE_MAX. Note that if
|
||||
* READ_FULL_FILE_FAIL_WHEN_LARGER we consider the specified size a safety limit, and thus also start
|
||||
* with LINE_MAX, under assumption the file is most likely much shorter. */
|
||||
if (n_next == 0)
|
||||
n_next = size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) ? size : LINE_MAX;
|
||||
|
||||
/* Never read more than we need to determine that our own limit is hit */
|
||||
if (n_next > READ_FULL_BYTES_MAX)
|
||||
n_next = READ_FULL_BYTES_MAX + 1;
|
||||
|
||||
if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -577,6 +587,11 @@ int read_full_stream_full(
|
|||
char *t;
|
||||
size_t k;
|
||||
|
||||
/* If we shall fail when reading overly large data, then read exactly one byte more than the
|
||||
* specified size at max, since that'll tell us if there's anymore data beyond the limit*/
|
||||
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
|
||||
n_next = size + 1;
|
||||
|
||||
if (flags & READ_FULL_FILE_SECURE) {
|
||||
t = malloc(n_next + 1);
|
||||
if (!t) {
|
||||
|
|
@ -610,14 +625,18 @@ int read_full_stream_full(
|
|||
if (feof(f))
|
||||
break;
|
||||
|
||||
if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
|
||||
if (size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER)) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
|
||||
assert(l == size);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
|
||||
|
||||
/* Safety check */
|
||||
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > size) {
|
||||
r = -E2BIG;
|
||||
goto finalize;
|
||||
}
|
||||
|
||||
if (n >= READ_FULL_BYTES_MAX) {
|
||||
r = -E2BIG;
|
||||
goto finalize;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ typedef enum {
|
|||
READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */
|
||||
READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
|
||||
READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */
|
||||
READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */
|
||||
} ReadFullFileFlags;
|
||||
|
||||
int fopen_unlocked(const char *path, const char *options, FILE **ret);
|
||||
|
|
|
|||
|
|
@ -74,16 +74,17 @@ typedef enum {
|
|||
|
||||
#define FORMAT_BYTES_MAX 16U
|
||||
|
||||
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) _warn_unused_result_;
|
||||
|
||||
_warn_unused_result_
|
||||
static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
|
||||
return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
|
||||
}
|
||||
|
||||
static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) {
|
||||
if (t == CGROUP_LIMIT_MAX) {
|
||||
(void) snprintf(buf, l, "%s", "infinity");
|
||||
return buf;
|
||||
}
|
||||
return format_bytes(buf, l, t);
|
||||
}
|
||||
/* Note: the lifetime of the compound literal is the immediately surrounding block,
|
||||
* see C11 §6.5.2.5, and
|
||||
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
|
||||
#define FORMAT_BYTES(t) format_bytes((char[FORMAT_BYTES_MAX]){}, FORMAT_BYTES_MAX, t)
|
||||
#define FORMAT_BYTES_FULL(t, flag) format_bytes_full((char[FORMAT_BYTES_MAX]){}, FORMAT_BYTES_MAX, t, flag)
|
||||
|
||||
#define FORMAT_BYTES_CGROUP_PROTECTION(t) (t == CGROUP_LIMIT_MAX ? "infinity" : FORMAT_BYTES(t))
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
|
|
@ -586,8 +585,6 @@ int get_files_in_directory(const char *path, char ***list) {
|
|||
return -errno;
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
dirent_ensure_type(d, de);
|
||||
|
||||
if (!dirent_is_file(de))
|
||||
continue;
|
||||
|
||||
|
|
@ -756,7 +753,8 @@ bool unsafe_transition(const struct stat *a, const struct stat *b) {
|
|||
}
|
||||
|
||||
static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
|
||||
_cleanup_free_ char *n1 = NULL, *n2 = NULL;
|
||||
_cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (!FLAGS_SET(flags, CHASE_WARN))
|
||||
return -ENOLINK;
|
||||
|
|
@ -764,9 +762,14 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags)
|
|||
(void) fd_get_path(a, &n1);
|
||||
(void) fd_get_path(b, &n2);
|
||||
|
||||
if (fstat(a, &st) == 0)
|
||||
user_a = uid_to_name(st.st_uid);
|
||||
if (fstat(b, &st) == 0)
|
||||
user_b = uid_to_name(st.st_uid);
|
||||
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
|
||||
"Detected unsafe path transition %s %s %s during canonicalization of %s.",
|
||||
strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path);
|
||||
"Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.",
|
||||
strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path);
|
||||
}
|
||||
|
||||
static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
|
||||
|
|
@ -1387,18 +1390,39 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
|
|||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int fsync_directory_of_file(int fd) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
_cleanup_close_ int dfd = -1;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* We only reasonably can do this for regular files and directories, hence check for that */
|
||||
/* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check
|
||||
* for the inode type first */
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
|
||||
if (dfd < 0)
|
||||
return -errno;
|
||||
|
||||
} else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other
|
||||
* types check O_PATH flag */
|
||||
int flags;
|
||||
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0)
|
||||
return -errno;
|
||||
|
||||
if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case
|
||||
* we can sensibly do what is requested. Otherwise this refers
|
||||
* to a socket, fifo or device node, where the concept of a
|
||||
* containing directory doesn't make too much sense. */
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
if (dfd < 0) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
r = fd_get_path(fd, &path);
|
||||
if (r < 0) {
|
||||
|
|
@ -1421,13 +1445,7 @@ int fsync_directory_of_file(int fd) {
|
|||
dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
|
||||
if (dfd < 0)
|
||||
return dfd;
|
||||
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
|
||||
if (dfd < 0)
|
||||
return -errno;
|
||||
} else
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
if (fsync(dfd) < 0)
|
||||
return -errno;
|
||||
|
|
@ -1479,12 +1497,56 @@ int fsync_path_at(int at_fd, const char *path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int fsync_parent_at(int at_fd, const char *path) {
|
||||
_cleanup_close_ int opened_fd = -1;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (at_fd != AT_FDCWD)
|
||||
return fsync_directory_of_file(at_fd);
|
||||
|
||||
opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fsync(opened_fd) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fsync_directory_of_file(opened_fd);
|
||||
}
|
||||
|
||||
int fsync_path_and_parent_at(int at_fd, const char *path) {
|
||||
_cleanup_close_ int opened_fd = -1;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (at_fd != AT_FDCWD)
|
||||
return fsync_full(at_fd);
|
||||
|
||||
opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
} else
|
||||
opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fsync_full(opened_fd);
|
||||
}
|
||||
|
||||
int syncfs_path(int atfd, const char *path) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(path);
|
||||
if (isempty(path)) {
|
||||
if (atfd != AT_FDCWD)
|
||||
return syncfs(atfd) < 0 ? -errno : 0;
|
||||
|
||||
fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK);
|
||||
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
} else
|
||||
fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -1516,91 +1578,6 @@ int open_parent(const char *path, int flags, mode_t mode) {
|
|||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
|
||||
_cleanup_free_ char *p = NULL, *uuids = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r, found_encrypted = false;
|
||||
|
||||
assert(sysfs_path);
|
||||
|
||||
if (depth_left == 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = path_join(sysfs_path, "dm/uuid");
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_one_line_file(p, &uuids);
|
||||
if (r != -ENOENT) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
|
||||
if (startswith(uuids, "CRYPT-"))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
|
||||
* subdir. */
|
||||
|
||||
p = mfree(p);
|
||||
p = path_join(sysfs_path, "slaves");
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
d = opendir(p);
|
||||
if (!d) {
|
||||
if (errno == ENOENT) /* Doesn't have underlying devices */
|
||||
return false;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
errno = 0;
|
||||
de = readdir_no_dot(d);
|
||||
if (!de) {
|
||||
if (errno != 0)
|
||||
return -errno;
|
||||
|
||||
break; /* No more underlying devices */
|
||||
}
|
||||
|
||||
q = path_join(p, de->d_name);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
r = blockdev_is_encrypted(q, depth_left - 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
|
||||
return false;
|
||||
|
||||
found_encrypted = true;
|
||||
}
|
||||
|
||||
return found_encrypted;
|
||||
}
|
||||
|
||||
int path_is_encrypted(const char *path) {
|
||||
char p[SYS_BLOCK_PATH_MAX(NULL)];
|
||||
dev_t devt;
|
||||
int r;
|
||||
|
||||
r = get_block_device(path, &devt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* doesn't have a block device */
|
||||
return false;
|
||||
|
||||
xsprintf_sys_block_path(p, NULL, devt);
|
||||
|
||||
return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int conservative_renameat(
|
||||
|
|
|
|||
|
|
@ -140,13 +140,13 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
|
|||
int fsync_directory_of_file(int fd);
|
||||
int fsync_full(int fd);
|
||||
int fsync_path_at(int at_fd, const char *path);
|
||||
int fsync_parent_at(int at_fd, const char *path);
|
||||
int fsync_path_and_parent_at(int at_fd, const char *path);
|
||||
|
||||
int syncfs_path(int atfd, const char *path);
|
||||
|
||||
int open_parent(const char *path, int flags, mode_t mode);
|
||||
|
||||
int path_is_encrypted(const char *path);
|
||||
|
||||
int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
|
||||
static inline int conservative_rename(const char *oldpath, const char *newpath) {
|
||||
return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ assert_cc(IDX_FIRST == _IDX_SWAP_END);
|
|||
assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
|
||||
|
||||
/* Storage space for the "swap" buckets.
|
||||
* All entry types can fit into a ordered_hashmap_entry. */
|
||||
* All entry types can fit into an ordered_hashmap_entry. */
|
||||
struct swap_entries {
|
||||
struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
|
||||
};
|
||||
|
|
@ -1763,6 +1763,9 @@ char** _hashmap_get_strv(HashmapBase *h) {
|
|||
Iterator i;
|
||||
unsigned idx, n;
|
||||
|
||||
if (!h)
|
||||
return new0(char*, 1);
|
||||
|
||||
sv = new(char*, n_entries(h)+1);
|
||||
if (!sv)
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo
|
|||
return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
|
||||
}
|
||||
|
||||
/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
|
||||
/* Since merging data from an OrderedHashmap into a Hashmap or vice-versa
|
||||
* should just work, allow this by having looser type-checking here. */
|
||||
int _hashmap_merge(Hashmap *h, Hashmap *other);
|
||||
#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
|
||||
|
|
|
|||
|
|
@ -570,38 +570,79 @@ int unbase64char(char c) {
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
ssize_t base64mem(const void *p, size_t l, char **out) {
|
||||
char *r, *z;
|
||||
static void maybe_line_break(char **x, char *start, size_t line_break) {
|
||||
size_t n;
|
||||
|
||||
assert(x);
|
||||
assert(*x);
|
||||
assert(start);
|
||||
assert(*x >= start);
|
||||
|
||||
if (line_break == SIZE_MAX)
|
||||
return;
|
||||
|
||||
n = *x - start;
|
||||
|
||||
if (n % (line_break + 1) == line_break)
|
||||
*((*x)++) = '\n';
|
||||
}
|
||||
|
||||
ssize_t base64mem_full(
|
||||
const void *p,
|
||||
size_t l,
|
||||
size_t line_break,
|
||||
char **out) {
|
||||
|
||||
const uint8_t *x;
|
||||
char *r, *z;
|
||||
size_t m;
|
||||
|
||||
assert(p || l == 0);
|
||||
assert(out);
|
||||
assert(line_break > 0);
|
||||
|
||||
/* three input bytes makes four output bytes, padding is added so we must round up */
|
||||
z = r = malloc(4 * (l + 2) / 3 + 1);
|
||||
m = 4 * (l + 2) / 3 + 1;
|
||||
|
||||
if (line_break != SIZE_MAX)
|
||||
m += m / line_break;
|
||||
|
||||
z = r = malloc(m);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
|
||||
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
|
||||
}
|
||||
|
||||
switch (l % 3) {
|
||||
case 2:
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = '=';
|
||||
|
||||
break;
|
||||
case 1:
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = '=';
|
||||
maybe_line_break(&z, r, line_break);
|
||||
*(z++) = '=';
|
||||
|
||||
break;
|
||||
|
|
@ -609,6 +650,7 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
|
|||
|
||||
*z = 0;
|
||||
*out = r;
|
||||
assert(z >= r); /* Let static analyzers know that the answer is non-negative. */
|
||||
return z - r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,11 @@ int unbase64char(char c) _const_;
|
|||
char *base32hexmem(const void *p, size_t l, bool padding);
|
||||
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
|
||||
|
||||
ssize_t base64mem(const void *p, size_t l, char **out);
|
||||
ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
|
||||
static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
|
||||
return base64mem_full(p, l, SIZE_MAX, ret);
|
||||
}
|
||||
|
||||
int base64_append(char **prefix, int plen,
|
||||
const void *p, size_t l,
|
||||
int margin, int width);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#define _weakref_(x) __attribute__((__weakref__(#x)))
|
||||
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
|
||||
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
|
||||
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
|
||||
#if __GNUC__ >= 7
|
||||
#define _fallthrough_ __attribute__((__fallthrough__))
|
||||
#else
|
||||
|
|
@ -222,7 +223,7 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
|
|||
* Contrary to strlen(), this is a constant expression.
|
||||
* @x: a string literal.
|
||||
*/
|
||||
#define STRLEN(x) (sizeof(""x"") - 1)
|
||||
#define STRLEN(x) ((unsigned) sizeof(""x"") - 1)
|
||||
|
||||
/*
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
|
|
@ -350,10 +351,10 @@ static inline int __coverity_check_and_return__(int condition) {
|
|||
* negative '-' prefix (hence works correctly on signed
|
||||
* types). Includes space for the trailing NUL. */
|
||||
#define DECIMAL_STR_MAX(type) \
|
||||
(2+(sizeof(type) <= 1 ? 3 : \
|
||||
sizeof(type) <= 2 ? 5 : \
|
||||
sizeof(type) <= 4 ? 10 : \
|
||||
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
(2U+(sizeof(type) <= 1 ? 3U : \
|
||||
sizeof(type) <= 2 ? 5U : \
|
||||
sizeof(type) <= 4 ? 10U : \
|
||||
sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
|
||||
#define DECIMAL_STR_WIDTH(x) \
|
||||
({ \
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/oom.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -738,7 +737,7 @@ int parse_oom_score_adjust(const char *s, int *ret) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX)
|
||||
if (!oom_score_adjust_is_valid(v))
|
||||
return -ERANGE;
|
||||
|
||||
*ret = v;
|
||||
|
|
|
|||
|
|
@ -472,8 +472,10 @@ char *path_startswith_full(const char *path, const char *prefix, bool accept_dot
|
|||
int path_compare(const char *a, const char *b) {
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
/* Order NULL before non-NULL */
|
||||
r = CMP(!!a, !!b);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* A relative path and an absolute path must not compare as equal.
|
||||
* Which one is sorted before the other does not really matter.
|
||||
|
|
@ -520,15 +522,9 @@ int path_compare(const char *a, const char *b) {
|
|||
}
|
||||
}
|
||||
|
||||
bool path_equal(const char *a, const char *b) {
|
||||
return path_compare(a, b) == 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
bool path_equal_or_files_same(const char *a, const char *b, int flags) {
|
||||
return path_equal(a, b) || files_same(a, b, flags) > 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
bool path_equal_filename(const char *a, const char *b) {
|
||||
_cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,11 @@ static inline char* path_startswith(const char *path, const char *prefix) {
|
|||
return path_startswith_full(path, prefix, true);
|
||||
}
|
||||
int path_compare(const char *a, const char *b) _pure_;
|
||||
bool path_equal(const char *a, const char *b) _pure_;
|
||||
|
||||
static inline bool path_equal(const char *a, const char *b) {
|
||||
return path_compare(a, b) == 0;
|
||||
}
|
||||
|
||||
bool path_equal_or_files_same(const char *a, const char *b, int flags);
|
||||
/* Compares only the last portion of the input paths, ie: the filenames */
|
||||
bool path_equal_filename(const char *a, const char *b);
|
||||
|
|
|
|||
|
|
@ -649,20 +649,23 @@ int get_process_environ(pid_t pid, char **env) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int get_process_ppid(pid_t pid, pid_t *_ppid) {
|
||||
int r;
|
||||
int get_process_ppid(pid_t pid, pid_t *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
long unsigned ppid;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(_ppid);
|
||||
|
||||
if (pid == 0 || pid == getpid_cached()) {
|
||||
*_ppid = getppid();
|
||||
if (ret)
|
||||
*ret = getppid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid == 1) /* PID 1 has no parent, shortcut this case */
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
p = procfs_file_alloca(pid, "stat");
|
||||
r = read_one_line_file(p, &line);
|
||||
if (r == -ENOENT)
|
||||
|
|
@ -670,9 +673,8 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Let's skip the pid and comm fields. The latter is enclosed
|
||||
* in () but does not escape any () in its value, so let's
|
||||
* skip over it manually */
|
||||
/* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
|
||||
* value, so let's skip over it manually */
|
||||
|
||||
p = strrchr(line, ')');
|
||||
if (!p)
|
||||
|
|
@ -686,10 +688,17 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
|
|||
&ppid) != 1)
|
||||
return -EIO;
|
||||
|
||||
if ((long unsigned) (pid_t) ppid != ppid)
|
||||
/* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for
|
||||
* processes originating in other namespaces that are inserted into a pidns. Return a recognizable
|
||||
* error in this case. */
|
||||
if (ppid == 0)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid)
|
||||
return -ERANGE;
|
||||
|
||||
*_ppid = (pid_t) ppid;
|
||||
if (ret)
|
||||
*ret = (pid_t) ppid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1036,30 +1045,6 @@ bool is_main_thread(void) {
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
_noreturn_ void freeze(void) {
|
||||
|
||||
log_close();
|
||||
|
||||
/* Make sure nobody waits for us on a socket anymore */
|
||||
(void) close_all_fds(NULL, 0);
|
||||
|
||||
sync();
|
||||
|
||||
/* Let's not freeze right away, but keep reaping zombies. */
|
||||
for (;;) {
|
||||
int r;
|
||||
siginfo_t si = {};
|
||||
|
||||
r = waitid(P_ALL, 0, &si, WEXITED);
|
||||
if (r < 0 && errno != EINTR)
|
||||
break;
|
||||
}
|
||||
|
||||
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
|
||||
for (;;)
|
||||
pause();
|
||||
}
|
||||
|
||||
bool oom_score_adjust_is_valid(int oa) {
|
||||
return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
|
||||
}
|
||||
|
|
@ -1272,7 +1257,7 @@ static void restore_sigsetp(sigset_t **ssp) {
|
|||
|
||||
int safe_fork_full(
|
||||
const char *name,
|
||||
const int except_fds[],
|
||||
int except_fds[],
|
||||
size_t n_except_fds,
|
||||
ForkFlags flags,
|
||||
pid_t *ret_pid) {
|
||||
|
|
@ -1467,7 +1452,7 @@ int safe_fork_full(
|
|||
int namespace_fork(
|
||||
const char *outer_name,
|
||||
const char *inner_name,
|
||||
const int except_fds[],
|
||||
int except_fds[],
|
||||
size_t n_except_fds,
|
||||
ForkFlags flags,
|
||||
int pidns_fd,
|
||||
|
|
@ -1483,7 +1468,8 @@ int namespace_fork(
|
|||
* process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
|
||||
* /proc/self/fd works correctly. */
|
||||
|
||||
r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
|
||||
r = safe_fork_full(outer_name, except_fds, n_except_fds,
|
||||
(flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
|
|
@ -1518,86 +1504,10 @@ int namespace_fork(
|
|||
return 1;
|
||||
}
|
||||
|
||||
int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
|
||||
bool stdout_is_tty, stderr_is_tty;
|
||||
size_t n, i;
|
||||
va_list ap;
|
||||
char **l;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Spawns a temporary TTY agent, making sure it goes away when we go away */
|
||||
|
||||
r = safe_fork_full(name,
|
||||
except,
|
||||
n_except,
|
||||
FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG,
|
||||
ret_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
/* In the child: */
|
||||
|
||||
stdout_is_tty = isatty(STDOUT_FILENO);
|
||||
stderr_is_tty = isatty(STDERR_FILENO);
|
||||
|
||||
if (!stdout_is_tty || !stderr_is_tty) {
|
||||
int fd;
|
||||
|
||||
/* Detach from stdout/stderr. and reopen
|
||||
* /dev/tty for them. This is important to
|
||||
* ensure that when systemctl is started via
|
||||
* popen() or a similar call that expects to
|
||||
* read EOF we actually do generate EOF and
|
||||
* not delay this indefinitely by because we
|
||||
* keep an unused copy of stdin around. */
|
||||
fd = open("/dev/tty", O_WRONLY);
|
||||
if (fd < 0) {
|
||||
log_error_errno(errno, "Failed to open /dev/tty: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
|
||||
log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
|
||||
log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
safe_close_above_stdio(fd);
|
||||
}
|
||||
|
||||
(void) rlimit_nofile_safe();
|
||||
|
||||
/* Count arguments */
|
||||
va_start(ap, path);
|
||||
for (n = 0; va_arg(ap, char*); n++)
|
||||
;
|
||||
va_end(ap);
|
||||
|
||||
/* Allocate strv */
|
||||
l = newa(char*, n + 1);
|
||||
|
||||
/* Fill in arguments */
|
||||
va_start(ap, path);
|
||||
for (i = 0; i <= n; i++)
|
||||
l[i] = va_arg(ap, char*);
|
||||
va_end(ap);
|
||||
|
||||
execv(path, l);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int set_oom_score_adjust(int value) {
|
||||
char t[DECIMAL_STR_MAX(int)];
|
||||
|
||||
sprintf(t, "%i", value);
|
||||
xsprintf(t, "%i", value);
|
||||
|
||||
return write_string_file("/proc/self/oom_score_adj", t,
|
||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ int pid_from_same_root_fs(pid_t pid);
|
|||
|
||||
bool is_main_thread(void);
|
||||
|
||||
_noreturn_ void freeze(void);
|
||||
|
||||
bool oom_score_adjust_is_valid(int oa);
|
||||
|
||||
#ifndef PERSONALITY_INVALID
|
||||
|
|
@ -170,15 +168,13 @@ typedef enum ForkFlags {
|
|||
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
|
||||
} ForkFlags;
|
||||
|
||||
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
||||
int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
||||
|
||||
static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
|
||||
return safe_fork_full(name, NULL, 0, flags, ret_pid);
|
||||
}
|
||||
|
||||
int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
|
||||
|
||||
int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_;
|
||||
int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
|
||||
|
||||
int set_oom_score_adjust(int value);
|
||||
|
||||
|
|
|
|||
|
|
@ -241,7 +241,6 @@ int signal_from_string(const char *s) {
|
|||
void nop_signal_handler(int sig) {
|
||||
/* nothing here */
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int signal_is_blocked(int sig) {
|
||||
sigset_t ss;
|
||||
|
|
@ -257,3 +256,39 @@ int signal_is_blocked(int sig) {
|
|||
|
||||
return r;
|
||||
}
|
||||
|
||||
int pop_pending_signal_internal(int sig, ...) {
|
||||
sigset_t ss;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
if (sig < 0) /* Empty list? */
|
||||
return -EINVAL;
|
||||
|
||||
if (sigemptyset(&ss) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build
|
||||
* parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
|
||||
* this.) */
|
||||
if (sig > 0 && sigaddset(&ss, sig) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Add all other signals */
|
||||
va_start(ap, sig);
|
||||
r = sigset_add_many_ap(&ss, ap);
|
||||
va_end(ap);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return r; /* Returns the signal popped */
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -62,3 +62,6 @@ static inline const char* signal_to_string_with_check(int n) {
|
|||
}
|
||||
|
||||
int signal_is_blocked(int sig);
|
||||
|
||||
int pop_pending_signal_internal(int sig, ...);
|
||||
#define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1)
|
||||
|
|
|
|||
|
|
@ -754,6 +754,22 @@ static const char* const ip_tos_table[] = {
|
|||
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
bool ifname_valid_char(char a) {
|
||||
if ((unsigned char) a >= 127U)
|
||||
return false;
|
||||
|
||||
if ((unsigned char) a <= 32U)
|
||||
return false;
|
||||
|
||||
if (IN_SET(a,
|
||||
':', /* colons are used by the legacy "alias" interface logic */
|
||||
'/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
|
||||
'%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
|
||||
bool numeric = true;
|
||||
|
||||
|
|
@ -787,16 +803,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
|
|||
return false;
|
||||
|
||||
for (const char *t = p; *t; t++) {
|
||||
if ((unsigned char) *t >= 127U)
|
||||
return false;
|
||||
|
||||
if ((unsigned char) *t <= 32U)
|
||||
return false;
|
||||
|
||||
if (IN_SET(*t,
|
||||
':', /* colons are used by the legacy "alias" interface logic */
|
||||
'/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
|
||||
'%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
|
||||
if (!ifname_valid_char(*t))
|
||||
return false;
|
||||
|
||||
numeric = numeric && (*t >= '0' && *t <= '9');
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ typedef enum {
|
|||
IFNAME_VALID_NUMERIC = 1 << 1,
|
||||
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
|
||||
} IfnameValidFlags;
|
||||
bool ifname_valid_char(char a);
|
||||
bool ifname_valid_full(const char *p, IfnameValidFlags flags);
|
||||
static inline bool ifname_valid(const char *p) {
|
||||
return ifname_valid_full(p, 0);
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ int path_is_read_only_fs(const char *path) {
|
|||
|
||||
return false;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int files_same(const char *filea, const char *fileb, int flags) {
|
||||
struct stat a, b;
|
||||
|
|
@ -176,6 +177,7 @@ int files_same(const char *filea, const char *fileb, int flags) {
|
|||
a.st_ino == b.st_ino;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
|
||||
assert(s);
|
||||
assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
|
||||
|
|
|
|||
|
|
@ -831,6 +831,7 @@ char **strv_reverse(char **l) {
|
|||
return l;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
char **strv_shell_escape(char **l, const char *bad) {
|
||||
char **s;
|
||||
|
||||
|
|
@ -850,6 +851,7 @@ char **strv_shell_escape(char **l, const char *bad) {
|
|||
|
||||
return l;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) {
|
||||
for (size_t i = 0; patterns && patterns[i]; i++)
|
||||
|
|
|
|||
|
|
@ -432,19 +432,37 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
|
|||
s = "left";
|
||||
}
|
||||
|
||||
if (d >= USEC_PER_YEAR)
|
||||
snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
|
||||
d / USEC_PER_YEAR,
|
||||
(d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
|
||||
else if (d >= USEC_PER_MONTH)
|
||||
snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
|
||||
d / USEC_PER_MONTH,
|
||||
(d % USEC_PER_MONTH) / USEC_PER_DAY, s);
|
||||
else if (d >= USEC_PER_WEEK)
|
||||
snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
|
||||
d / USEC_PER_WEEK,
|
||||
(d % USEC_PER_WEEK) / USEC_PER_DAY, s);
|
||||
else if (d >= 2*USEC_PER_DAY)
|
||||
if (d >= USEC_PER_YEAR) {
|
||||
usec_t years = d / USEC_PER_YEAR;
|
||||
usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
|
||||
|
||||
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
years,
|
||||
years == 1 ? "year" : "years",
|
||||
months,
|
||||
months == 1 ? "month" : "months",
|
||||
s);
|
||||
} else if (d >= USEC_PER_MONTH) {
|
||||
usec_t months = d / USEC_PER_MONTH;
|
||||
usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
|
||||
|
||||
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
months,
|
||||
months == 1 ? "month" : "months",
|
||||
days,
|
||||
days == 1 ? "day" : "days",
|
||||
s);
|
||||
} else if (d >= USEC_PER_WEEK) {
|
||||
usec_t weeks = d / USEC_PER_WEEK;
|
||||
usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
|
||||
|
||||
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
weeks,
|
||||
weeks == 1 ? "week" : "weeks",
|
||||
days,
|
||||
days == 1 ? "day" : "days",
|
||||
s);
|
||||
} else if (d >= 2*USEC_PER_DAY)
|
||||
snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
|
||||
else if (d >= 25*USEC_PER_HOUR)
|
||||
snprintf(buf, l, "1 day " USEC_FMT "h %s",
|
||||
|
|
@ -539,14 +557,12 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
|
|||
|
||||
/* Let's see if we should shows this in dot notation */
|
||||
if (t < USEC_PER_MINUTE && b > 0) {
|
||||
usec_t cc;
|
||||
signed char j;
|
||||
signed char j = 0;
|
||||
|
||||
j = 0;
|
||||
for (cc = table[i].usec; cc > 1; cc /= 10)
|
||||
for (usec_t cc = table[i].usec; cc > 1; cc /= 10)
|
||||
j++;
|
||||
|
||||
for (cc = accuracy; cc > 1; cc /= 10) {
|
||||
for (usec_t cc = accuracy; cc > 1; cc /= 10) {
|
||||
b /= 10;
|
||||
j--;
|
||||
}
|
||||
|
|
@ -1249,75 +1265,127 @@ int parse_nsec(const char *t, nsec_t *nsec) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int get_timezones(char ***ret) {
|
||||
static int get_timezones_from_zone1970_tab(char ***ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_strv_free_ char **zones = NULL;
|
||||
size_t n_zones = 0;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
zones = strv_new("UTC");
|
||||
if (!zones)
|
||||
return -ENOMEM;
|
||||
|
||||
n_zones = 1;
|
||||
|
||||
f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
|
||||
if (f) {
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL, *w = NULL;
|
||||
char *p;
|
||||
size_t k;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
p = strstrip(line);
|
||||
|
||||
if (isempty(p) || *p == '#')
|
||||
continue;
|
||||
|
||||
/* Skip over country code */
|
||||
p += strcspn(p, WHITESPACE);
|
||||
p += strspn(p, WHITESPACE);
|
||||
|
||||
/* Skip over coordinates */
|
||||
p += strcspn(p, WHITESPACE);
|
||||
p += strspn(p, WHITESPACE);
|
||||
|
||||
/* Found timezone name */
|
||||
k = strcspn(p, WHITESPACE);
|
||||
if (k <= 0)
|
||||
continue;
|
||||
|
||||
w = strndup(p, k);
|
||||
if (!w)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!GREEDY_REALLOC(zones, n_zones + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
zones[n_zones++] = TAKE_PTR(w);
|
||||
zones[n_zones] = NULL;
|
||||
}
|
||||
|
||||
strv_sort(zones);
|
||||
strv_uniq(zones);
|
||||
|
||||
} else if (errno != ENOENT)
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
*ret = TAKE_PTR(zones);
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
const char *p = line;
|
||||
|
||||
/* Line format is:
|
||||
* 'country codes' 'coordinates' 'timezone' 'comments' */
|
||||
r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
/* Lines that start with # are comments. */
|
||||
if (*cc == '#')
|
||||
continue;
|
||||
|
||||
r = strv_extend(&zones, tz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(zones);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_timezones_from_tzdata_zi(char ***ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_strv_free_ char **zones = NULL;
|
||||
int r;
|
||||
|
||||
f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
const char *p = line;
|
||||
|
||||
/* The only lines we care about are Zone and Link lines.
|
||||
* Zone line format is:
|
||||
* 'Zone' 'timezone' ...
|
||||
* Link line format is:
|
||||
* 'Link' 'target' 'alias'
|
||||
* See 'man zic' for more detail. */
|
||||
r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
char *tz;
|
||||
if (IN_SET(*type, 'Z', 'z'))
|
||||
/* Zone lines have timezone in field 1. */
|
||||
tz = f1;
|
||||
else if (IN_SET(*type, 'L', 'l'))
|
||||
/* Link lines have timezone in field 2. */
|
||||
tz = f2;
|
||||
else
|
||||
/* Not a line we care about. */
|
||||
continue;
|
||||
|
||||
r = strv_extend(&zones, tz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(zones);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_timezones(char ***ret) {
|
||||
_cleanup_strv_free_ char **zones = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = get_timezones_from_tzdata_zi(&zones);
|
||||
if (r == -ENOENT) {
|
||||
log_debug_errno(r, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
|
||||
r = get_timezones_from_zone1970_tab(&zones);
|
||||
if (r == -ENOENT)
|
||||
log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
|
||||
}
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
/* Always include UTC */
|
||||
r = strv_extend(&zones, "UTC");
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
strv_sort(zones);
|
||||
strv_uniq(zones);
|
||||
|
||||
*ret = TAKE_PTR(zones);
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
bool timezone_is_valid(const char *name, int log_level) {
|
||||
int verify_timezone(const char *name, int log_level) {
|
||||
bool slash = false;
|
||||
const char *p, *t;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
|
@ -1325,26 +1393,26 @@ bool timezone_is_valid(const char *name, int log_level) {
|
|||
int r;
|
||||
|
||||
if (isempty(name))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
/* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
|
||||
if (streq(name, "UTC"))
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
if (name[0] == '/')
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
for (p = name; *p; p++) {
|
||||
if (!(*p >= '0' && *p <= '9') &&
|
||||
!(*p >= 'a' && *p <= 'z') &&
|
||||
!(*p >= 'A' && *p <= 'Z') &&
|
||||
!IN_SET(*p, '-', '_', '+', '/'))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
if (*p == '/') {
|
||||
|
||||
if (slash)
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
slash = true;
|
||||
} else
|
||||
|
|
@ -1352,38 +1420,31 @@ bool timezone_is_valid(const char *name, int log_level) {
|
|||
}
|
||||
|
||||
if (slash)
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
if (p - name >= PATH_MAX)
|
||||
return false;
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
t = strjoina("/usr/share/zoneinfo/", name);
|
||||
|
||||
fd = open(t, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
|
||||
return false;
|
||||
}
|
||||
if (fd < 0)
|
||||
return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
|
||||
|
||||
r = fd_verify_regular(fd);
|
||||
if (r < 0) {
|
||||
log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
|
||||
return false;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
|
||||
|
||||
r = loop_read_exact(fd, buf, 4, false);
|
||||
if (r < 0) {
|
||||
log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
|
||||
return false;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
|
||||
|
||||
/* Magic from tzfile(5) */
|
||||
if (memcmp(buf, "TZif", 4) != 0) {
|
||||
log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
|
||||
return false;
|
||||
}
|
||||
if (memcmp(buf, "TZif", 4) != 0)
|
||||
return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Timezone file '%s' has wrong magic bytes", t);
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool clock_boottime_supported(void) {
|
||||
|
|
|
|||
|
|
@ -111,20 +111,31 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
|
|||
|
||||
usec_t timespec_load(const struct timespec *ts) _pure_;
|
||||
nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
|
||||
struct timespec *timespec_store(struct timespec *ts, usec_t u);
|
||||
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n);
|
||||
struct timespec* timespec_store(struct timespec *ts, usec_t u);
|
||||
struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
|
||||
|
||||
usec_t timeval_load(const struct timeval *tv) _pure_;
|
||||
struct timeval *timeval_store(struct timeval *tv, usec_t u);
|
||||
struct timeval* timeval_store(struct timeval *tv, usec_t u);
|
||||
|
||||
char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style);
|
||||
char *format_timestamp_relative(char *buf, size_t l, usec_t t);
|
||||
char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
|
||||
char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
|
||||
char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
|
||||
char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
|
||||
|
||||
static inline char *format_timestamp(char *buf, size_t l, usec_t t) {
|
||||
_warn_unused_result_
|
||||
static inline char* format_timestamp(char *buf, size_t l, usec_t t) {
|
||||
return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY);
|
||||
}
|
||||
|
||||
/* Note: the lifetime of the compound literal is the immediately surrounding block,
|
||||
* see C11 §6.5.2.5, and
|
||||
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
|
||||
#define FORMAT_TIMESTAMP(t) format_timestamp((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t)
|
||||
#define FORMAT_TIMESTAMP_RELATIVE(t) \
|
||||
format_timestamp_relative((char[FORMAT_TIMESTAMP_RELATIVE_MAX]){}, FORMAT_TIMESTAMP_RELATIVE_MAX, t)
|
||||
#define FORMAT_TIMESPAN(t, accuracy) format_timespan((char[FORMAT_TIMESPAN_MAX]){}, FORMAT_TIMESPAN_MAX, t, accuracy)
|
||||
#define FORMAT_TIMESTAMP_STYLE(t, style) \
|
||||
format_timestamp_style((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t, style)
|
||||
|
||||
int parse_timestamp(const char *t, usec_t *usec);
|
||||
|
||||
int parse_sec(const char *t, usec_t *usec);
|
||||
|
|
@ -134,7 +145,10 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit);
|
|||
int parse_nsec(const char *t, nsec_t *nsec);
|
||||
|
||||
int get_timezones(char ***l);
|
||||
bool timezone_is_valid(const char *name, int log_level);
|
||||
int verify_timezone(const char *name, int log_level);
|
||||
static inline bool timezone_is_valid(const char *name, int log_level) {
|
||||
return verify_timezone(name, log_level) >= 0;
|
||||
}
|
||||
|
||||
bool clock_boottime_supported(void);
|
||||
bool clock_supported(clockid_t clock);
|
||||
|
|
@ -153,9 +167,8 @@ usec_t jiffies_to_usec(uint32_t jiffies);
|
|||
bool in_utc_timezone(void);
|
||||
|
||||
static inline usec_t usec_add(usec_t a, usec_t b) {
|
||||
|
||||
/* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't
|
||||
* overflow. */
|
||||
/* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output,
|
||||
* and doesn't overflow. */
|
||||
|
||||
if (a > USEC_INFINITY - b) /* overflow check */
|
||||
return USEC_INFINITY;
|
||||
|
|
@ -164,7 +177,6 @@ static inline usec_t usec_add(usec_t a, usec_t b) {
|
|||
}
|
||||
|
||||
static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) {
|
||||
|
||||
if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */
|
||||
return USEC_INFINITY;
|
||||
if (timestamp < delta)
|
||||
|
|
@ -181,14 +193,14 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
|
|||
}
|
||||
|
||||
#if SIZEOF_TIME_T == 8
|
||||
/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
|
||||
* territory. However, since we want to stay away from this in all timezones we take one day off. */
|
||||
#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
|
||||
/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
|
||||
* year territory. However, since we want to stay away from this in all timezones we take one day off. */
|
||||
# define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
|
||||
#elif SIZEOF_TIME_T == 4
|
||||
/* With a 32bit time_t we can't go beyond 2038... */
|
||||
#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
|
||||
# define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
|
||||
#else
|
||||
#error "Yuck, time_t is neither 4 nor 8 bytes wide?"
|
||||
# error "Yuck, time_t is neither 4 nor 8 bytes wide?"
|
||||
#endif
|
||||
|
||||
int time_change_fd(void);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue