diff options
| author | Michael Biebl <biebl@debian.org> | 2023-08-09 21:55:35 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2023-08-09 21:55:35 +0200 |
| commit | 05e4a733f2141995181a551854d5df929f084adf (patch) | |
| tree | 83bb937740a6667525ba0df046748ecaa829c269 /src/libnm-systemd-core | |
| parent | 14b0f3a9dc9ea90d60a3b057350fd4d637dc021a (diff) | |
New upstream version 1.44.0 upstream/1.44.0
Diffstat (limited to 'src/libnm-systemd-core')
24 files changed, 1423 insertions, 318 deletions
diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index 753ca149..6175e42c 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -21,6 +21,7 @@ libnm_systemd_core = static_library( include_directories( 'sd-adapt-core', 'src/libsystemd-network', + 'src/libsystemd/sd-device', 'src/libsystemd/sd-event', 'src/systemd', ), diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c index e4d2d2e1..c5ef63eb 100644 --- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c +++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c @@ -8,6 +8,7 @@ #include "nm-sd-adapt-core.h" #include "fd-util.h" +#include "sd-device.h" /*****************************************************************************/ @@ -19,3 +20,17 @@ asynchronous_close(int fd) } /*****************************************************************************/ + +sd_device * +sd_device_ref(sd_device *self) +{ + g_return_val_if_fail(!self, self); + return self; +} + +sd_device * +sd_device_unref(sd_device *self) +{ + g_return_val_if_fail(!self, self); + return self; +} diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h index 7ce1f8c9..9c317801 100644 --- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h +++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h @@ -40,6 +40,8 @@ #include <sys/syscall.h> #include <sys/ioctl.h> +#define HAVE_EPOLL_PWAIT2 0 + /* Missing in Linux 3.2.0, in Ubuntu 12.04 */ #ifndef BPF_XOR #define BPF_XOR 0xa0 @@ -85,8 +87,14 @@ sd_notify(int unset_environment, const char *state) #include "sd-id128.h" #include "sparse-endian.h" #include "async.h" -#include "util.h" #endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ +/*****************************************************************************/ + +struct sd_device; + +struct sd_device *sd_device_ref(struct sd_device *self); +struct sd_device *sd_device_unref(struct sd_device *self); + #endif /* __NM_SD_ADAPT_CORE_H__ */ diff --git a/src/libnm-systemd-core/sd-adapt-core/sd-device.h b/src/libnm-systemd-core/sd-adapt-core/sd-device.h deleted file mode 100644 index 637892c2..00000000 --- a/src/libnm-systemd-core/sd-adapt-core/sd-device.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/libnm-systemd-core/sd-adapt-core/device-util.h b/src/libnm-systemd-core/sd-adapt-core/sd-messages.h index 637892c2..637892c2 100644 --- a/src/libnm-systemd-core/sd-adapt-core/device-util.h +++ b/src/libnm-systemd-core/sd-adapt-core/sd-messages.h diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c index 2636d796..05d0585a 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c @@ -6,15 +6,11 @@ #include <net/ethernet.h> #include <net/if_arp.h> -#include "sd-device.h" -#include "sd-id128.h" - #include "dhcp-identifier.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" #include "string-table.h" -#include "udev-util.h" #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) @@ -213,49 +209,21 @@ int dhcp_identifier_set_duid( #endif /* NM_IGNORED */ int dhcp_identifier_set_iaid( - int ifindex, + sd_device *dev, const struct hw_addr_data *hw_addr, bool legacy_unstable_byteorder, - bool use_mac, void *ret) { #if 0 /* NM_IGNORED */ - /* name is a pointer to memory in the sd_device struct, so must - * have the same scope */ - _cleanup_(sd_device_unrefp) sd_device *device = NULL; const char *name = NULL; uint32_t id32; uint64_t id; - int r; - assert(ifindex > 0); assert(hw_addr); assert(ret); - if (udev_available() && !use_mac) { - /* udev should be around */ - - r = sd_device_new_from_ifindex(&device, ifindex); - if (r < 0) - return r; - - r = sd_device_get_is_initialized(device); - if (r < 0) - return r; - if (r == 0) - /* not yet ready */ - return -EBUSY; - - r = device_is_renaming(device); - if (r < 0) - return r; - if (r > 0) - /* device is under renaming */ - return -EBUSY; - - name = net_get_persistent_name(device); - } - + if (dev) + name = net_get_persistent_name(dev); if (name) id = siphash24(name, strlen(name), HASH_KEY.bytes); else diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h index 8acb8c32..523dfc4a 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "sd-device.h" #include "sd-id128.h" #include "ether-addr-util.h" @@ -66,10 +67,9 @@ int dhcp_identifier_set_duid( struct duid *ret_duid, size_t *ret_len); int dhcp_identifier_set_iaid( - int ifindex, + sd_device *dev, const struct hw_addr_data *hw_addr, bool legacy_unstable_byteorder, - bool use_mac, void *ret); const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h index 65f6cb05..fa43f28e 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -48,6 +48,8 @@ struct sd_dhcp6_client { int event_priority; int fd; + sd_device *dev; + DHCP6State state; bool information_request; usec_t information_request_time_usec; @@ -77,8 +79,9 @@ struct sd_dhcp6_client { sd_dhcp6_client_callback_t callback; void *userdata; + bool send_release; - /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ + /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */ bool test_mode; }; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c index beace4c7..7b17dbc1 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c @@ -25,7 +25,7 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), .in6.sin6_scope_id = ifindex, }; - _cleanup_close_ int s = -1; + _cleanup_close_ int s = -EBADF; int r; assert(ifindex > 0); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index 14e9c806..23cf8a89 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -497,13 +497,18 @@ int dhcp6_option_parse( } int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) { + DHCP6Status status; + assert(data || data_len == 0); if (data_len < sizeof(uint16_t)) return -EBADMSG; + status = unaligned_read_be16(data); + if (ret_status_message) { - char *msg; + _cleanup_free_ char *msg = NULL; + const char *s; /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415. * Let's escape unsafe characters for safety. */ @@ -511,10 +516,14 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s if (!msg) return -ENOMEM; - *ret_status_message = msg; + s = dhcp6_message_status_to_string(status); + if (s && !strextend_with_separator(&msg, ": ", s)) + return -ENOMEM; + + *ret_status_message = TAKE_PTR(msg); } - return unaligned_read_be16(data); + return status; } static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { @@ -540,9 +549,8 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return r; if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA address or PD prefix option with non-zero status: %s%s%s", - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); + "Received an IA address or PD prefix option with non-zero status%s%s", + isempty(msg) ? "." : ": ", strempty(msg)); if (r < 0) /* Let's log but ignore the invalid status option. */ log_dhcp6_client_errno(client, r, @@ -748,9 +756,8 @@ int dhcp6_option_parse_ia( return r; if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA option with non-zero status: %s%s%s", - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); + "Received an IA option with non-zero status%s%s", + isempty(msg) ? "." : ": ", strempty(msg)); if (r < 0) log_dhcp6_client_errno(client, r, "Received an IA option with an invalid status sub option, ignoring: %m"); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c index 4e945777..059930eb 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c @@ -13,6 +13,7 @@ static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { [DHCP6_STATE_BOUND] = "bound", [DHCP6_STATE_RENEW] = "renew", [DHCP6_STATE_REBIND] = "rebind", + [DHCP6_STATE_STOPPING] = "stopping", }; DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h index 18217691..c70f9320 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h @@ -58,6 +58,7 @@ typedef enum DHCP6State { DHCP6_STATE_BOUND, DHCP6_STATE_RENEW, DHCP6_STATE_REBIND, + DHCP6_STATE_STOPPING, _DHCP6_STATE_MAX, _DHCP6_STATE_INVALID = -EINVAL, } DHCP6State; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index db159660..a2605b71 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -17,6 +17,7 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" +#include "device-util.h" #include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" @@ -309,9 +310,8 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { if (client->iaid_set) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr, + r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr, /* legacy_unstable_byteorder = */ true, - /* use_mac = */ client->test_mode, &iaid); if (r < 0) return r; @@ -508,6 +508,14 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) { return 0; } +int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) { + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + client->send_release = enable; + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -596,7 +604,8 @@ static int client_append_common_options_in_managed_mode( DHCP6_STATE_SOLICITATION, DHCP6_STATE_REQUEST, DHCP6_STATE_RENEW, - DHCP6_STATE_REBIND)); + DHCP6_STATE_REBIND, + DHCP6_STATE_STOPPING)); assert(buf); assert(*buf); assert(offset); @@ -613,9 +622,11 @@ static int client_append_common_options_in_managed_mode( return r; } - r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); - if (r < 0) - return r; + if (client->state != DHCP6_STATE_STOPPING) { + r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); + if (r < 0) + return r; + } r = dhcp6_option_append_user_class(buf, offset, client->user_class); if (r < 0) @@ -646,6 +657,8 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) return DHCP6_MESSAGE_RENEW; case DHCP6_STATE_REBIND: return DHCP6_MESSAGE_REBIND; + case DHCP6_STATE_STOPPING: + return DHCP6_MESSAGE_RELEASE; default: assert_not_reached(); } @@ -689,6 +702,9 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off req_opts = p; break; + case DHCP6_STATE_STOPPING: + return 0; + default: n = client->n_req_opts; req_opts = client->req_opts; @@ -700,6 +716,22 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); } +static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) { + assert(client); + assert(buf); + assert(*buf); + assert(offset); + + if (!client->mudurl) + return 0; + + if (client->state == DHCP6_STATE_STOPPING) + return 0; + + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); +} + int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ uint8_t *buf = NULL; struct in6_addr all_servers = @@ -745,7 +777,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - + case DHCP6_STATE_STOPPING: r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); @@ -763,18 +795,15 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { return r; break; - case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: + case DHCP6_STATE_STOPPED: default: assert_not_reached(); } - if (client->mudurl) { - r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6, - strlen(client->mudurl), client->mudurl); - if (r < 0) - return r; - } + r = client_append_mudurl(client, &buf, &offset); + if (r < 0) + return r; r = client_append_oro(client, &buf, &offset); if (r < 0) @@ -866,6 +895,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda break; case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: case DHCP6_STATE_BOUND: default: assert_not_reached(); @@ -921,6 +951,7 @@ static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); break; case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: case DHCP6_STATE_BOUND: default: assert_not_reached(); @@ -1255,7 +1286,6 @@ static int client_receive_message( .msg_control = &control, .msg_controllen = sizeof(control), }; - struct cmsghdr *cmsg; triple_timestamp t = {}; _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr *server_address = NULL; @@ -1299,12 +1329,9 @@ static int client_receive_message( server_address = &sa.in6.sin6_addr; } - CMSG_FOREACH(cmsg, &msg) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SO_TIMESTAMP && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) - triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) (void *) CMSG_DATA(cmsg))); - } + struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval); + if (tv) + triple_timestamp_from_realtime(&t, timeval_load(tv)); if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) return 0; @@ -1329,6 +1356,7 @@ static int client_receive_message( case DHCP6_STATE_BOUND: case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: default: assert_not_reached(); } @@ -1336,10 +1364,37 @@ static int client_receive_message( return 0; } +static int client_send_release(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + + assert(client); + + if (!client->send_release) + return 0; + + if (sd_dhcp6_client_get_lease(client, &lease) < 0) + return 0; + + if (!lease->ia_na && !lease->ia_pd) + return 0; + + client_set_state(client, DHCP6_STATE_STOPPING); + return dhcp6_client_send_message(client); +} + int sd_dhcp6_client_stop(sd_dhcp6_client *client) { + int r; + if (!client) return 0; + /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client + * engine is about to release its UDP socket unconditionally. */ + r = client_send_release(client); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Failed to send DHCP6 release message, ignoring: %m"); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); client->receive_message = sd_event_source_unref(client->receive_message); @@ -1456,6 +1511,12 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { return client->event; } +int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev) { + assert_return(client, -EINVAL); + + return device_unref_and_replace(client->dev, dev); +} + static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { if (!client) return NULL; @@ -1471,6 +1532,8 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { client->fd = safe_close(client->fd); + sd_device_unref(client->dev); + free(client->req_opts); free(client->fqdn); free(client->mudurl); @@ -1501,7 +1564,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .ia_pd.type = SD_DHCP6_OPTION_IA_PD, .ifindex = -1, .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, - .fd = -1, + .fd = -EBADF, .rapid_commit = true, }; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index 5dcd2eb2..3e25b4e8 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -512,13 +512,11 @@ static int dhcp6_lease_parse_message( r = dhcp6_option_parse_status(optval, optlen, &msg); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m"); - if (r > 0) return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r), - "Received %s message with non-zero status: %s%s%s", + "Received %s message with non-zero status%s%s", dhcp6_message_type_to_string(message->type), - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); + isempty(msg) ? "." : ": ", strempty(msg)); break; } case SD_DHCP6_OPTION_IA_NA: { diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h new file mode 100644 index 00000000..a1b5e91e --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdbool.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sd-device.h" + +#include "log.h" +#include "macro.h" + +#define device_unref_and_replace(a, b) \ + unref_and_replace_full(a, b, sd_device_ref, sd_device_unref) + +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ + for (key = sd_device_get_property_first(device, &(value)); \ + key; \ + key = sd_device_get_property_next(device, &(value))) + +#define FOREACH_DEVICE_TAG(device, tag) \ + for (tag = sd_device_get_tag_first(device); \ + tag; \ + tag = sd_device_get_tag_next(device)) + +#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \ + for (tag = sd_device_get_current_tag_first(device); \ + tag; \ + tag = sd_device_get_current_tag_next(device)) + +#define FOREACH_DEVICE_SYSATTR(device, attr) \ + for (attr = sd_device_get_sysattr_first(device); \ + attr; \ + attr = sd_device_get_sysattr_next(device)) + +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ + for (devlink = sd_device_get_devlink_first(device); \ + devlink; \ + devlink = sd_device_get_devlink_next(device)) + +#define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \ + for (child = sd_device_get_child_first(device, suffix_ptr); \ + child; \ + child = sd_device_get_child_next(device, suffix_ptr)) + +#define FOREACH_DEVICE_CHILD(device, child) \ + _FOREACH_DEVICE_CHILD(device, child, NULL) + +#define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \ + _FOREACH_DEVICE_CHILD(device, child, &suffix) + +#define FOREACH_DEVICE(enumerator, device) \ + for (device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_device_next(enumerator)) + +#define FOREACH_SUBSYSTEM(enumerator, device) \ + for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_subsystem_next(enumerator)) + +#define log_device_full_errno_zerook(device, level, error, ...) \ + ({ \ + const char *_sysname = NULL; \ + sd_device *_d = (device); \ + int _level = (level), _e = (error); \ + \ + if (_d && _unlikely_(log_get_max_level() >= LOG_PRI(_level))) \ + (void) sd_device_get_sysname(_d, &_sysname); \ + log_object_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, \ + _sysname ? "DEVICE=" : NULL, _sysname, \ + NULL, NULL, __VA_ARGS__); \ + }) + +#define log_device_full_errno(device, level, error, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_device_full_errno_zerook(device, level, _error, __VA_ARGS__); \ + }) + +#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__) + +#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__) +#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__) +#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__) +#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__) +#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__) + +#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__) +#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__) +#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__) +#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__) +#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__) + +int devname_from_devnum(mode_t mode, dev_t devnum, char **ret); +static inline int devname_from_stat_rdev(const struct stat *st, char **ret) { + assert(st); + return devname_from_devnum(st->st_mode, st->st_rdev, ret); +} +int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret); + +char** device_make_log_fields(sd_device *device); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h index 6092652d..f4e38d78 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h @@ -27,6 +27,7 @@ typedef enum EventSourceType { SOURCE_EXIT, SOURCE_WATCHDOG, SOURCE_INOTIFY, + SOURCE_MEMORY_PRESSURE, _SOURCE_EVENT_SOURCE_TYPE_MAX, _SOURCE_EVENT_SOURCE_TYPE_INVALID = -EINVAL, } EventSourceType; @@ -129,6 +130,17 @@ struct sd_event_source { struct inode_data *inode_data; LIST_FIELDS(sd_event_source, by_inode_data); } inotify; + struct { + int fd; + sd_event_handler_t callback; + void *write_buffer; + size_t write_buffer_size; + uint32_t events, revents; + LIST_FIELDS(sd_event_source, write_list); + bool registered:1; + bool locked:1; + bool in_write_list:1; + } memory_pressure; }; }; diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c index ebbd4422..663296f4 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c @@ -48,7 +48,7 @@ int event_reset_time( return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "sd-event: Current clock id %i of event source \"%s\" is different from specified one %i.", (int)c, - strna((*s)->description ? : description), + strna((*s)->description ?: description), (int)clock); r = sd_event_source_set_time(*s, usec); @@ -114,7 +114,7 @@ int event_reset_time_relative( #if 0 /* NM_IGNORED */ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) { _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; int r; assert(e); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index 70776077..5c7ba182 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -9,6 +9,7 @@ #include "sd-daemon.h" #include "sd-event.h" #include "sd-id128.h" +#include "sd-messages.h" #include "alloc-util.h" #include "env-util.h" @@ -17,14 +18,24 @@ #include "fs-util.h" #include "glyph-util.h" #include "hashmap.h" +#include "hexdecoct.h" #include "list.h" +#include "logarithm.h" #include "macro.h" +#include "mallinfo-util.h" #include "memory-util.h" +#include "missing_magic.h" #include "missing_syscall.h" +#include "missing_threads.h" +#include "origin-id.h" +#include "path-util.h" #include "prioq.h" #include "process-util.h" +#include "psi-util.h" #include "set.h" #include "signal-util.h" +#include "socket-util.h" +#include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strxcpyx.h" @@ -53,7 +64,7 @@ static bool event_source_is_offline(sd_event_source *s) { static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { [SOURCE_IO] = "io", [SOURCE_TIME_REALTIME] = "realtime", - [SOURCE_TIME_BOOTTIME] = "bootime", + [SOURCE_TIME_BOOTTIME] = "boottime", [SOURCE_TIME_MONOTONIC] = "monotonic", [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm", [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm", @@ -64,6 +75,7 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] [SOURCE_EXIT] = "exit", [SOURCE_WATCHDOG] = "watchdog", [SOURCE_INOTIFY] = "inotify", + [SOURCE_MEMORY_PRESSURE] = "memory-pressure", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); @@ -86,7 +98,8 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); SOURCE_TIME_BOOTTIME_ALARM, \ SOURCE_SIGNAL, \ SOURCE_DEFER, \ - SOURCE_INOTIFY) + SOURCE_INOTIFY, \ + SOURCE_MEMORY_PRESSURE) /* 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 @@ -131,7 +144,10 @@ struct sd_event { /* A list of inotify objects that already have events buffered which aren't processed yet */ LIST_HEAD(struct inotify_data, buffered_inotify_data_list); - pid_t original_pid; + /* A list of memory pressure event sources that still need their subscription string written */ + LIST_HEAD(sd_event_source, memory_pressure_write_list); + + uint64_t origin_id; uint64_t iteration; triple_timestamp timestamp; @@ -161,6 +177,8 @@ struct sd_event { unsigned delays[sizeof(usec_t) * 8]; }; +DEFINE_PRIVATE_ORIGIN_ID_HELPERS(sd_event, event); + static thread_local sd_event *default_event = NULL; static void source_disconnect(sd_event_source *s); @@ -379,25 +397,25 @@ _public_ int sd_event_new(sd_event** ret) { *e = (sd_event) { .n_ref = 1, - .epoll_fd = -1, - .watchdog_fd = -1, + .epoll_fd = -EBADF, + .watchdog_fd = -EBADF, .realtime.wakeup = WAKEUP_CLOCK_DATA, - .realtime.fd = -1, + .realtime.fd = -EBADF, .realtime.next = USEC_INFINITY, .boottime.wakeup = WAKEUP_CLOCK_DATA, - .boottime.fd = -1, + .boottime.fd = -EBADF, .boottime.next = USEC_INFINITY, .monotonic.wakeup = WAKEUP_CLOCK_DATA, - .monotonic.fd = -1, + .monotonic.fd = -EBADF, .monotonic.next = USEC_INFINITY, .realtime_alarm.wakeup = WAKEUP_CLOCK_DATA, - .realtime_alarm.fd = -1, + .realtime_alarm.fd = -EBADF, .realtime_alarm.next = USEC_INFINITY, .boottime_alarm.wakeup = WAKEUP_CLOCK_DATA, - .boottime_alarm.fd = -1, + .boottime_alarm.fd = -EBADF, .boottime_alarm.next = USEC_INFINITY, .perturb = USEC_INFINITY, - .original_pid = getpid_cached(), + .origin_id = origin_id_query(), }; r = prioq_ensure_allocated(&e->pending, pending_prioq_compare); @@ -426,7 +444,31 @@ fail: return r; } -DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free); +/* Define manually so we can add the origin check */ +_public_ sd_event *sd_event_ref(sd_event *e) { + if (!e) + return NULL; + if (event_origin_changed(e)) + return NULL; + + e->n_ref++; + + return e; +} + +_public_ sd_event* sd_event_unref(sd_event *e) { + if (!e) + return NULL; + if (event_origin_changed(e)) + return NULL; + + assert(e->n_ref > 0); + if (--e->n_ref > 0) + return NULL; + + return event_free(e); +} + #define PROTECT_EVENT(e) \ _unused_ _cleanup_(sd_event_unrefp) sd_event *_ref = sd_event_ref(e); @@ -436,20 +478,11 @@ _public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) { return sd_event_source_unref(s); } -static bool event_pid_changed(sd_event *e) { - assert(e); - - /* We don't support people creating an event loop and keeping - * it around over a fork(). Let's complain. */ - - return e->original_pid != getpid_cached(); -} - static void source_io_unregister(sd_event_source *s) { assert(s); assert(s->type == SOURCE_IO); - if (event_pid_changed(s->event)) + if (event_origin_changed(s->event)) return; if (!s->io.registered) @@ -490,7 +523,7 @@ static void source_child_pidfd_unregister(sd_event_source *s) { assert(s); assert(s->type == SOURCE_CHILD); - if (event_pid_changed(s->event)) + if (event_origin_changed(s->event)) return; if (!s->child.registered) @@ -525,6 +558,67 @@ static int source_child_pidfd_register(sd_event_source *s, int enabled) { return 0; } +static void source_memory_pressure_unregister(sd_event_source *s) { + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + + if (event_origin_changed(s->event)) + return; + + if (!s->memory_pressure.registered) + return; + + if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->memory_pressure.fd, NULL) < 0) + log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m", + strna(s->description), event_source_type_to_string(s->type)); + + s->memory_pressure.registered = false; +} + +static int source_memory_pressure_register(sd_event_source *s, int enabled) { + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + assert(enabled != SD_EVENT_OFF); + + struct epoll_event ev = { + .events = s->memory_pressure.write_buffer_size > 0 ? EPOLLOUT : + (s->memory_pressure.events | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0)), + .data.ptr = s, + }; + + if (epoll_ctl(s->event->epoll_fd, + s->memory_pressure.registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, + s->memory_pressure.fd, &ev) < 0) + return -errno; + + s->memory_pressure.registered = true; + return 0; +} + +#if 0 /* NM_IGNORED */ +static void source_memory_pressure_add_to_write_list(sd_event_source *s) { + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + + if (s->memory_pressure.in_write_list) + return; + + LIST_PREPEND(memory_pressure.write_list, s->event->memory_pressure_write_list, s); + s->memory_pressure.in_write_list = true; +} +#endif /* NM_IGNORED */ + +static void source_memory_pressure_remove_from_write_list(sd_event_source *s) { + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + + if (!s->memory_pressure.in_write_list) + return; + + LIST_REMOVE(memory_pressure.write_list, s->event->memory_pressure_write_list, s); + s->memory_pressure.in_write_list = false; +} + static clockid_t event_source_type_to_clock(EventSourceType t) { switch (t) { @@ -622,7 +716,7 @@ static int event_make_signal_data( assert(e); - if (event_pid_changed(e)) + if (event_origin_changed(e)) return -ECHILD; if (e->signal_sources && e->signal_sources[sig]) @@ -644,7 +738,7 @@ static int event_make_signal_data( *d = (struct signal_data) { .wakeup = WAKEUP_SIGNAL_DATA, - .fd = -1, + .fd = -EBADF, .priority = priority, }; @@ -660,7 +754,9 @@ static int event_make_signal_data( ss_copy = d->sigset; assert_se(sigaddset(&ss_copy, sig) >= 0); - r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); + r = signalfd(d->fd >= 0 ? d->fd : -1, /* the first arg must be -1 or a valid signalfd */ + &ss_copy, + SFD_NONBLOCK|SFD_CLOEXEC); if (r < 0) { r = -errno; goto fail; @@ -717,7 +813,7 @@ static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig return; } - if (event_pid_changed(e)) + if (event_origin_changed(e)) return; assert(d->fd >= 0); @@ -880,7 +976,7 @@ static void source_disconnect(sd_event_source *s) { break; case SOURCE_CHILD: - if (event_pid_changed(s->event)) + if (event_origin_changed(s->event)) s->child.process_owned = false; if (s->child.pid > 0) { @@ -946,6 +1042,11 @@ static void source_disconnect(sd_event_source *s) { break; } + case SOURCE_MEMORY_PRESSURE: + source_memory_pressure_remove_from_write_list(s); + source_memory_pressure_unregister(s); + break; + default: assert_not_reached(); } @@ -1016,6 +1117,11 @@ static sd_event_source* source_free(sd_event_source *s) { s->child.pidfd = safe_close(s->child.pidfd); } + if (s->type == SOURCE_MEMORY_PRESSURE) { + s->memory_pressure.fd = safe_close(s->memory_pressure.fd); + s->memory_pressure.write_buffer = mfree(s->memory_pressure.write_buffer); + } + if (s->destroy_callback) s->destroy_callback(s->userdata); @@ -1074,22 +1180,48 @@ static int source_set_pending(sd_event_source *s, bool b) { } static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { + + /* Let's allocate exactly what we need. Note that the difference of the smallest event source + * structure to the largest is 144 bytes on x86-64 at the time of writing, i.e. more than two cache + * lines. */ + static const size_t size_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { + [SOURCE_IO] = endoffsetof_field(sd_event_source, io), + [SOURCE_TIME_REALTIME] = endoffsetof_field(sd_event_source, time), + [SOURCE_TIME_BOOTTIME] = endoffsetof_field(sd_event_source, time), + [SOURCE_TIME_MONOTONIC] = endoffsetof_field(sd_event_source, time), + [SOURCE_TIME_REALTIME_ALARM] = endoffsetof_field(sd_event_source, time), + [SOURCE_TIME_BOOTTIME_ALARM] = endoffsetof_field(sd_event_source, time), + [SOURCE_SIGNAL] = endoffsetof_field(sd_event_source, signal), + [SOURCE_CHILD] = endoffsetof_field(sd_event_source, child), + [SOURCE_DEFER] = endoffsetof_field(sd_event_source, defer), + [SOURCE_POST] = endoffsetof_field(sd_event_source, post), + [SOURCE_EXIT] = endoffsetof_field(sd_event_source, exit), + [SOURCE_INOTIFY] = endoffsetof_field(sd_event_source, inotify), + [SOURCE_MEMORY_PRESSURE] = endoffsetof_field(sd_event_source, memory_pressure), + }; + sd_event_source *s; assert(e); + assert(type >= 0); + assert(type < _SOURCE_EVENT_SOURCE_TYPE_MAX); + assert(size_table[type] > 0); - s = new(sd_event_source, 1); + s = malloc0(size_table[type]); if (!s) return NULL; - - *s = (struct sd_event_source) { - .n_ref = 1, - .event = e, - .floating = floating, - .type = type, - .pending_index = PRIOQ_IDX_NULL, - .prepare_index = PRIOQ_IDX_NULL, - }; + /* We use expand_to_usable() here to tell gcc that it should consider this an object of the full + * size, even if we only allocate the initial part we need. */ + s = expand_to_usable(s, sizeof(sd_event_source)); + + /* Note: we cannot use compound initialization here, because sizeof(sd_event_source) is likely larger + * than what we allocated here. */ + s->n_ref = 1; + s->event = e; + s->floating = floating; + s->type = type; + s->pending_index = PRIOQ_IDX_NULL; + s->prepare_index = PRIOQ_IDX_NULL; if (!floating) sd_event_ref(e); @@ -1122,7 +1254,7 @@ _public_ int sd_event_add_io( assert_return(fd >= 0, -EBADF); assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!callback) callback = io_exit_callback; @@ -1150,22 +1282,21 @@ _public_ int sd_event_add_io( } static void initialize_perturb(sd_event *e) { - sd_id128_t bootid = {}; + sd_id128_t id = {}; - /* When we sleep for longer, we try to realign the wakeup to - the same time within each minute/second/250ms, so that - events all across the system can be coalesced into a single - CPU wakeup. However, let's take some system-specific - randomness for this value, so that in a network of systems - with synced clocks timer events are distributed a - bit. Here, we calculate a perturbation usec offset from the - boot ID. */ + /* When we sleep for longer, we try to realign the wakeup to the same time within each + * minute/second/250ms, so that events all across the system can be coalesced into a single CPU + * wakeup. However, let's take some system-specific randomness for this value, so that in a network + * of systems with synced clocks timer events are distributed a bit. Here, we calculate a + * perturbation usec offset from the boot ID (or machine ID if failed, e.g. /proc is not mounted). */ if (_likely_(e->perturb != USEC_INFINITY)) return; - if (sd_id128_get_boot(&bootid) >= 0) - e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE; + if (sd_id128_get_boot(&id) >= 0 || sd_id128_get_machine(&id) >= 0) + e->perturb = (id.qwords[0] ^ id.qwords[1]) % USEC_PER_MINUTE; + else + e->perturb = 0; /* This is a super early process without /proc and /etc ?? */ } static int event_setup_timer_fd( @@ -1179,7 +1310,7 @@ static int event_setup_timer_fd( if (_likely_(d->fd >= 0)) return 0; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC); if (fd < 0) @@ -1270,7 +1401,7 @@ _public_ int sd_event_add_time( assert_return(e = event_resolve(e), -ENOPKG); assert_return(accuracy != UINT64_MAX, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!clock_supported(clock)) /* Checks whether the kernel supports the clock */ return -EOPNOTSUPP; @@ -1358,7 +1489,7 @@ _public_ int sd_event_add_signal( assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); /* Let's make sure our special flag stays outside of the valid signal range */ assert_cc(_NSIG < SD_EVENT_SIGNAL_PROCMASK); @@ -1468,7 +1599,7 @@ _public_ int sd_event_add_child( assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); assert_return(options != 0, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!callback) callback = child_exit_callback; @@ -1516,7 +1647,7 @@ _public_ int sd_event_add_child( } else s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */ } else - s->child.pidfd = -1; + s->child.pidfd = -EBADF; if (EVENT_SOURCE_WATCH_PIDFD(s)) { /* We have a pidfd and we only want to watch for exit */ @@ -1566,7 +1697,7 @@ _public_ int sd_event_add_child_pidfd( assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); assert_return(options != 0, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!callback) callback = child_exit_callback; @@ -1648,7 +1779,7 @@ _public_ int sd_event_add_defer( assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!callback) callback = generic_exit_callback; @@ -1684,7 +1815,7 @@ _public_ int sd_event_add_post( assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!callback) callback = generic_exit_callback; @@ -1722,7 +1853,7 @@ _public_ int sd_event_add_exit( assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); r = prioq_ensure_allocated(&e->exit, exit_prioq_compare); if (r < 0) @@ -1748,6 +1879,262 @@ _public_ int sd_event_add_exit( return 0; } +#if 0 /* NM_IGNORED */ +_public_ int sd_event_trim_memory(void) { + int r; + + /* A default implementation of a memory pressure callback. Simply releases our own allocation caches + * and glibc's. This is automatically used when people call sd_event_add_memory_pressure() with a + * NULL callback parameter. */ + + log_debug("Memory pressure event, trimming malloc() memory."); + +#if HAVE_GENERIC_MALLINFO + generic_mallinfo before_mallinfo = generic_mallinfo_get(); +#endif + + usec_t before_timestamp = now(CLOCK_MONOTONIC); + hashmap_trim_pools(); + r = malloc_trim(0); + usec_t after_timestamp = now(CLOCK_MONOTONIC); + + if (r > 0) + log_debug("Successfully trimmed some memory."); + else + log_debug("Couldn't trim any memory."); + + usec_t period = after_timestamp - before_timestamp; + +#if HAVE_GENERIC_MALLINFO + generic_mallinfo after_mallinfo = generic_mallinfo_get(); + size_t l = LESS_BY((size_t) before_mallinfo.hblkhd, (size_t) after_mallinfo.hblkhd) + + LESS_BY((size_t) before_mallinfo.arena, (size_t) after_mallinfo.arena); + log_struct(LOG_DEBUG, + LOG_MESSAGE("Memory trimming took %s, returned %s to OS.", + FORMAT_TIMESPAN(period, 0), + FORMAT_BYTES(l)), + "MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR, + "TRIMMED_BYTES=%zu", l, + "TRIMMED_USEC=" USEC_FMT, period); +#else + log_struct(LOG_DEBUG, + LOG_MESSAGE("Memory trimming took %s.", + FORMAT_TIMESPAN(period, 0)), + "MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR, + "TRIMMED_USEC=" USEC_FMT, period); +#endif + + return 0; +} + +static int memory_pressure_callback(sd_event_source *s, void *userdata) { + assert(s); + + sd_event_trim_memory(); + return 0; +} + +_public_ int sd_event_add_memory_pressure( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + _cleanup_free_ char *w = NULL; + _cleanup_(source_freep) sd_event_source *s = NULL; + _cleanup_close_ int path_fd = -EBADF, fd = -EBADF; + _cleanup_free_ void *write_buffer = NULL; + const char *watch, *watch_fallback = NULL, *env; + size_t write_buffer_size = 0; + struct stat st; + uint32_t events; + bool locked; + int r; + + assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_origin_changed(e), -ECHILD); + + if (!callback) + callback = memory_pressure_callback; + + s = source_new(e, !ret, SOURCE_MEMORY_PRESSURE); + if (!s) + return -ENOMEM; + + s->wakeup = WAKEUP_EVENT_SOURCE; + s->memory_pressure.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ON; + s->memory_pressure.fd = -EBADF; + + env = secure_getenv("MEMORY_PRESSURE_WATCH"); + if (env) { + if (isempty(env) || path_equal(env, "/dev/null")) + return log_debug_errno(SYNTHETIC_ERRNO(EHOSTDOWN), + "Memory pressure logic is explicitly disabled via $MEMORY_PRESSURE_WATCH."); + + if (!path_is_absolute(env) || !path_is_normalized(env)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "$MEMORY_PRESSURE_WATCH set to invalid path: %s", env); + + watch = env; + + env = secure_getenv("MEMORY_PRESSURE_WRITE"); + if (env) { + r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size); + if (r < 0) + return r; + } + + locked = true; + } else { + + r = is_pressure_supported(); + if (r < 0) + return r; + if (r == 0) + return -EOPNOTSUPP; + + /* By default we want to watch memory pressure on the local cgroup, but we'll fall back on + * the system wide pressure if for some reason we cannot (which could be: memory controller + * not delegated to us, or PSI simply not available in the kernel). On legacy cgroupv1 we'll + * only use the system-wide logic. */ + r = cg_all_unified(); + if (r < 0) + return r; + if (r == 0) + watch = "/proc/pressure/memory"; + else { + _cleanup_free_ char *cg = NULL; + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cg); + if (r < 0) + return r; + + w = path_join("/sys/fs/cgroup", cg, "memory.pressure"); + if (!w) + return -ENOMEM; + + watch = w; + watch_fallback = "/proc/pressure/memory"; + } + + /* Android uses three levels in its userspace low memory killer logic: + * some 70000 1000000 + * some 100000 1000000 + * full 70000 1000000 + * + * GNOME's low memory monitor uses: + * some 70000 1000000 + * some 100000 1000000 + * full 100000 1000000 + * + * We'll default to the middle level that both agree on. Except we do it on a 2s window + * (i.e. 200ms per 2s, rather than 100ms per 1s), because that's the window duration the + * kernel will allow us to do unprivileged, also in the future. */ + if (asprintf((char**) &write_buffer, + "%s " USEC_FMT " " USEC_FMT, + MEMORY_PRESSURE_DEFAULT_TYPE, + MEMORY_PRESSURE_DEFAULT_THRESHOLD_USEC, + MEMORY_PRESSURE_DEFAULT_WINDOW_USEC) < 0) + return -ENOMEM; + + write_buffer_size = strlen(write_buffer) + 1; + locked = false; + } + + path_fd = open(watch, O_PATH|O_CLOEXEC); + if (path_fd < 0) { + if (errno != ENOENT) + return -errno; + + /* We got ENOENT. Three options now: try the fallback if we have one, or return the error as + * is (if based on user/env config), or return -EOPNOTSUPP (because we picked the path, and + * the PSI service apparently is not supported) */ + if (!watch_fallback) + return locked ? -ENOENT : -EOPNOTSUPP; + + path_fd = open(watch_fallback, O_PATH|O_CLOEXEC); + if (path_fd < 0) { + if (errno == ENOENT) /* PSI is not available in the kernel even under the fallback path? */ + return -EOPNOTSUPP; + return -errno; + } + } + + if (fstat(path_fd, &st) < 0) + return -errno; + + if (S_ISSOCK(st.st_mode)) { + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + r = connect_unix_path(fd, path_fd, NULL); + if (r < 0) + return r; + + events = EPOLLIN; + + } else if (S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode)) { + fd = fd_reopen(path_fd, (write_buffer_size > 0 ? O_RDWR : O_RDONLY) |O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + return fd; + + if (S_ISREG(st.st_mode)) { + struct statfs sfs; + + /* If this is a regular file validate this is a procfs or cgroupfs file, where we look for EPOLLPRI */ + + if (fstatfs(fd, &sfs) < 0) + return -errno; + + if (!is_fs_type(&sfs, PROC_SUPER_MAGIC) && + !is_fs_type(&sfs, CGROUP2_SUPER_MAGIC)) + return -ENOTTY; + + events = EPOLLPRI; + } else + /* For fifos and char devices just watch for EPOLLIN */ + events = EPOLLIN; + + } else if (S_ISDIR(st.st_mode)) + return -EISDIR; + else + return -EBADF; + + s->memory_pressure.fd = TAKE_FD(fd); + s->memory_pressure.write_buffer = TAKE_PTR(write_buffer); + s->memory_pressure.write_buffer_size = write_buffer_size; + s->memory_pressure.events = events; + s->memory_pressure.locked = locked; + + /* So here's the thing: if we are talking to PSI we need to write the watch string before adding the + * fd to epoll (if we ignore this, then the watch won't work). Hence we'll not actually register the + * fd with the epoll right-away. Instead, we just add the event source to a list of memory pressure + * event sources on which writes must be executed before the first event loop iteration is + * executed. (We could also write the data here, right away, but we want to give the caller the + * freedom to call sd_event_source_set_memory_pressure_type() and + * sd_event_source_set_memory_pressure_rate() before we write it. */ + + if (s->memory_pressure.write_buffer_size > 0) + source_memory_pressure_add_to_write_list(s); + else { + r = source_memory_pressure_register(s, s->enabled); + if (r < 0) + return r; + } + + if (ret) + *ret = s; + TAKE_PTR(s); + + return 0; +} +#endif /* NM_IGNORED */ + static void event_free_inotify_data(sd_event *e, struct inotify_data *d) { assert(e); @@ -1766,7 +2153,7 @@ static void event_free_inotify_data(sd_event *e, struct inotify_data *d) { assert_se(hashmap_remove(e->inotify_data, &d->priority) == d); if (d->fd >= 0) { - if (!event_pid_changed(e) && + if (!event_origin_changed(e) && epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, d->fd, NULL) < 0) log_debug_errno(errno, "Failed to remove inotify fd from epoll, ignoring: %m"); @@ -1780,7 +2167,7 @@ static int event_make_inotify_data( int64_t priority, struct inotify_data **ret) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; struct inotify_data *d; int r; @@ -1877,7 +2264,7 @@ static void event_free_inode_data( if (d->inotify_data) { if (d->wd >= 0) { - if (d->inotify_data->fd >= 0 && !event_pid_changed(e)) { + if (d->inotify_data->fd >= 0 && !event_origin_changed(e)) { /* So here's a problem. At the time this runs the watch descriptor might already be * invalidated, because an IN_IGNORED event might be queued right the moment we enter * the syscall. Hence, whenever we get EINVAL, ignore it entirely, since it's a very @@ -1977,7 +2364,7 @@ static int event_make_inode_data( .dev = dev, .ino = ino, .wd = -1, - .fd = -1, + .fd = -EBADF, .inotify_data = inotify_data, }; @@ -2073,7 +2460,7 @@ static int event_add_inotify_fd_internal( sd_event_inotify_handler_t callback, void *userdata) { - _cleanup_close_ int donated_fd = donate ? fd : -1; + _cleanup_close_ int donated_fd = donate ? fd : -EBADF; _cleanup_(source_freep) sd_event_source *s = NULL; struct inotify_data *inotify_data = NULL; struct inode_data *inode_data = NULL; @@ -2084,7 +2471,7 @@ static int event_add_inotify_fd_internal( assert_return(e = event_resolve(e), -ENOPKG); assert_return(fd >= 0, -EBADF); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!callback) callback = inotify_exit_callback; @@ -2175,9 +2562,9 @@ _public_ int sd_event_add_inotify( assert_return(path, -EINVAL); - fd = open(path, O_PATH|O_CLOEXEC| - (mask & IN_ONLYDIR ? O_DIRECTORY : 0)| - (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0)); + fd = open(path, O_PATH | O_CLOEXEC | + (mask & IN_ONLYDIR ? O_DIRECTORY : 0) | + (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0)); if (fd < 0) return -errno; @@ -2217,7 +2604,7 @@ DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event_source, sd_event_source, event_sou _public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); return free_and_strdup(&s->description, description); } @@ -2225,7 +2612,6 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des _public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { assert_return(s, -EINVAL); assert_return(description, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); if (!s->description) return -ENXIO; @@ -2236,6 +2622,7 @@ _public_ int sd_event_source_get_description(sd_event_source *s, const char **de _public_ sd_event *sd_event_source_get_event(sd_event_source *s) { assert_return(s, NULL); + assert_return(!event_origin_changed(s->event), NULL); return s->event; } @@ -2244,7 +2631,7 @@ _public_ int sd_event_source_get_pending(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type != SOURCE_EXIT, -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->pending; } @@ -2252,7 +2639,7 @@ _public_ int sd_event_source_get_pending(sd_event_source *s) { _public_ int sd_event_source_get_io_fd(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->io.fd; } @@ -2263,7 +2650,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { assert_return(s, -EINVAL); assert_return(fd >= 0, -EBADF); assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->io.fd == fd) return 0; @@ -2296,6 +2683,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { _public_ int sd_event_source_get_io_fd_own(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->io.owned; } @@ -2303,6 +2691,7 @@ _public_ int sd_event_source_get_io_fd_own(sd_event_source *s) { _public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); s->io.owned = own; return 0; @@ -2312,7 +2701,7 @@ _public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) assert_return(s, -EINVAL); assert_return(events, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *events = s->io.events; return 0; @@ -2325,7 +2714,7 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) assert_return(s->type == SOURCE_IO, -EDOM); assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); /* edge-triggered updates are never skipped, so we can reset edges */ if (s->io.events == events && !(events & EPOLLET)) @@ -2351,7 +2740,7 @@ _public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revent assert_return(revents, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); assert_return(s->pending, -ENODATA); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *revents = s->io.revents; return 0; @@ -2360,14 +2749,14 @@ _public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revent _public_ int sd_event_source_get_signal(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_SIGNAL, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->signal.sig; } _public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) { assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *priority = s->priority; return 0; @@ -2381,7 +2770,7 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) assert_return(s, -EINVAL); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->priority == priority) return 0; @@ -2481,7 +2870,7 @@ _public_ int sd_event_source_get_enabled(sd_event_source *s, int *ret) { return false; assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); if (ret) *ret = s->enabled; @@ -2539,6 +2928,10 @@ static int event_source_offline( prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); break; + case SOURCE_MEMORY_PRESSURE: + source_memory_pressure_unregister(s); + break; + case SOURCE_TIME_REALTIME: case SOURCE_TIME_BOOTTIME: case SOURCE_TIME_MONOTONIC: @@ -2626,6 +3019,13 @@ static int event_source_online( s->event->n_online_child_sources++; break; + case SOURCE_MEMORY_PRESSURE: + r = source_memory_pressure_register(s, enabled); + if (r < 0) + return r; + + break; + case SOURCE_TIME_REALTIME: case SOURCE_TIME_BOOTTIME: case SOURCE_TIME_MONOTONIC: @@ -2664,7 +3064,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { return 0; assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); /* If we are dead anyway, we are fine with turning off sources, but everything else needs to fail. */ if (s->event->state == SD_EVENT_FINISHED) @@ -2696,7 +3096,7 @@ _public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { assert_return(s, -EINVAL); assert_return(usec, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *usec = s->time.next; return 0; @@ -2708,7 +3108,7 @@ _public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { assert_return(s, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); r = source_set_pending(s, false); if (r < 0) @@ -2726,6 +3126,10 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec assert_return(s, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); + + if (usec == USEC_INFINITY) + return sd_event_source_set_time(s, USEC_INFINITY); r = sd_event_now(s->event, event_source_type_to_clock(s->type), &t); if (r < 0) @@ -2742,7 +3146,7 @@ _public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *use assert_return(s, -EINVAL); assert_return(usec, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *usec = s->time.accuracy; return 0; @@ -2755,7 +3159,7 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec assert_return(usec != UINT64_MAX, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); r = source_set_pending(s, false); if (r < 0) @@ -2774,7 +3178,7 @@ _public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock assert_return(s, -EINVAL); assert_return(clock, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *clock = event_source_type_to_clock(s->type); return 0; @@ -2784,7 +3188,7 @@ _public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { assert_return(s, -EINVAL); assert_return(pid, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *pid = s->child.pid; return 0; @@ -2793,7 +3197,7 @@ _public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { _public_ int sd_event_source_get_child_pidfd(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->child.pidfd < 0) return -EOPNOTSUPP; @@ -2804,7 +3208,7 @@ _public_ int sd_event_source_get_child_pidfd(sd_event_source *s) { _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo_t *si, unsigned flags) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(SIGNAL_VALID(sig), -EINVAL); /* If we already have seen indication the process exited refuse sending a signal early. This way we @@ -2849,6 +3253,7 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons _public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->child.pidfd < 0) return -EOPNOTSUPP; @@ -2859,6 +3264,7 @@ _public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) { _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->child.pidfd < 0) return -EOPNOTSUPP; @@ -2870,6 +3276,7 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) { _public_ int sd_event_source_get_child_process_own(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->child.process_owned; } @@ -2877,6 +3284,7 @@ _public_ int sd_event_source_get_child_process_own(sd_event_source *s) { _public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); s->child.process_owned = own; return 0; @@ -2886,7 +3294,7 @@ _public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask assert_return(s, -EINVAL); assert_return(mask, -EINVAL); assert_return(s->type == SOURCE_INOTIFY, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); *mask = s->inotify.mask; return 0; @@ -2898,7 +3306,7 @@ _public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t assert_return(s, -EINVAL); assert_return(s->type != SOURCE_EXIT, -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->prepare == callback) return 0; @@ -2926,6 +3334,7 @@ _public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t _public_ void* sd_event_source_get_userdata(sd_event_source *s) { assert_return(s, NULL); + assert_return(!event_origin_changed(s->event), NULL); return s->userdata; } @@ -2934,6 +3343,7 @@ _public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) void *ret; assert_return(s, NULL); + assert_return(!event_origin_changed(s->event), NULL); ret = s->userdata; s->userdata = userdata; @@ -3180,7 +3590,7 @@ static int event_arm_timer( assert_se(d->fd >= 0); if (t == 0) { - /* We don' want to disarm here, just mean some time looooong ago. */ + /* We don't want to disarm here, just mean some time looooong ago. */ its.it_value.tv_sec = 0; its.it_value.tv_nsec = 1; } else @@ -3604,6 +4014,115 @@ static int process_inotify(sd_event *e) { return done; } +static int process_memory_pressure(sd_event_source *s, uint32_t revents) { + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + + if (s->pending) + s->memory_pressure.revents |= revents; + else + s->memory_pressure.revents = revents; + + return source_set_pending(s, true); +} + +static int source_memory_pressure_write(sd_event_source *s) { + ssize_t n; + int r; + + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + + /* once we start writing, the buffer is locked, we allow no further changes. */ + s->memory_pressure.locked = true; + + if (s->memory_pressure.write_buffer_size > 0) { + n = write(s->memory_pressure.fd, s->memory_pressure.write_buffer, s->memory_pressure.write_buffer_size); + if (n < 0) { + if (!ERRNO_IS_TRANSIENT(errno)) { + /* If kernel is built with CONFIG_PSI_DEFAULT_DISABLED it will expose PSI + * files, but then generates EOPNOSUPP on read() and write() (instead of on + * open()!). This sucks hard, since we can only detect this kind of failure + * so late. Let's make the best of it, and turn off the event source like we + * do for failed event source handlers. */ + + log_debug_errno(errno, "Writing memory pressure settings to kernel failed, disabling memory pressure event source: %m"); + assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); + return 0; + } + + n = 0; + } + } else + n = 0; + + assert(n >= 0); + + if ((size_t) n == s->memory_pressure.write_buffer_size) { + s->memory_pressure.write_buffer = mfree(s->memory_pressure.write_buffer); + + if (n > 0) { + s->memory_pressure.write_buffer_size = 0; + + /* Update epoll events mask, since we have now written everything and don't care for EPOLLOUT anymore */ + r = source_memory_pressure_register(s, s->enabled); + if (r < 0) + return r; + } + } else if (n > 0) { + _cleanup_free_ void *c = NULL; + + assert((size_t) n < s->memory_pressure.write_buffer_size); + + c = memdup((uint8_t*) s->memory_pressure.write_buffer + n, s->memory_pressure.write_buffer_size - n); + if (!c) + return -ENOMEM; + + free_and_replace(s->memory_pressure.write_buffer, c); + s->memory_pressure.write_buffer_size -= n; + return 1; + } + + return 0; +} + +static int source_memory_pressure_initiate_dispatch(sd_event_source *s) { + int r; + + assert(s); + assert(s->type == SOURCE_MEMORY_PRESSURE); + + r = source_memory_pressure_write(s); + if (r < 0) + return r; + if (r > 0) + return 1; /* if we wrote something, then don't continue with dispatching user dispatch + * function. Instead, shortcut it so that we wait for next EPOLLOUT immediately. */ + + /* No pending incoming IO? Then let's not continue further */ + if ((s->memory_pressure.revents & (EPOLLIN|EPOLLPRI)) == 0) { + + /* Treat IO errors on the notifier the same ways errors returned from a callback */ + if ((s->memory_pressure.revents & (EPOLLHUP|EPOLLERR|EPOLLRDHUP)) != 0) + return -EIO; + + return 1; /* leave dispatch, we already processed everything */ + } + + if (s->memory_pressure.revents & EPOLLIN) { + uint8_t pipe_buf[PIPE_BUF]; + ssize_t n; + + /* If the fd is readable, then flush out anything that might be queued */ + + n = read(s->memory_pressure.fd, pipe_buf, sizeof(pipe_buf)); + if (n < 0 && !ERRNO_IS_TRANSIENT(errno)) + return -errno; + } + + return 0; /* go on, dispatch to user callback */ +} + static int source_dispatch(sd_event_source *s) { EventSourceType saved_type; sd_event *saved_event; @@ -3652,6 +4171,16 @@ static int source_dispatch(sd_event_source *s) { } } + if (s->type == SOURCE_MEMORY_PRESSURE) { + r = source_memory_pressure_initiate_dispatch(s); + if (r == -EIO) /* handle EIO errors similar to callback errors */ + goto finish; + if (r < 0) + return r; + if (r > 0) /* already handled */ + return 1; + } + if (s->enabled == SD_EVENT_ONESHOT) { r = sd_event_source_set_enabled(s, SD_EVENT_OFF); if (r < 0) @@ -3738,6 +4267,10 @@ static int source_dispatch(sd_event_source *s) { break; } + case SOURCE_MEMORY_PRESSURE: + r = s->memory_pressure.callback(s, s->userdata); + break; + case SOURCE_WATCHDOG: case _SOURCE_EVENT_SOURCE_TYPE_MAX: case _SOURCE_EVENT_SOURCE_TYPE_INVALID: @@ -3746,6 +4279,7 @@ static int source_dispatch(sd_event_source *s) { s->dispatching = false; +finish: if (r < 0) { log_debug_errno(r, "Event source %s (type %s) returned error, %s: %m", strna(s->description), @@ -3777,12 +4311,9 @@ static int event_prepare(sd_event *e) { break; s->prepare_iteration = e->iteration; - r = prioq_reshuffle(e->prepare, s, &s->prepare_index); - if (r < 0) - return r; + prioq_reshuffle(e->prepare, s, &s->prepare_index); assert(s->prepare); - s->dispatching = true; r = s->prepare(s, s->userdata); s->dispatching = false; @@ -3899,12 +4430,36 @@ static void event_close_inode_data_fds(sd_event *e) { } } +static int event_memory_pressure_write_list(sd_event *e) { + int r; + + assert(e); + + for (;;) { + sd_event_source *s; + + s = LIST_POP(memory_pressure.write_list, e->memory_pressure_write_list); + if (!s) + break; + + assert(s->type == SOURCE_MEMORY_PRESSURE); + assert(s->memory_pressure.write_buffer_size > 0); + s->memory_pressure.in_write_list = false; + + r = source_memory_pressure_write(s); + if (r < 0) + return r; + } + + return 0; +} + _public_ int sd_event_prepare(sd_event *e) { int r; assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -3927,6 +4482,10 @@ _public_ int sd_event_prepare(sd_event *e) { if (r < 0) return r; + r = event_memory_pressure_write_list(e); + if (r < 0) + return r; + r = event_arm_timer(e, &e->realtime); if (r < 0) return r; @@ -3972,15 +4531,18 @@ static int epoll_wait_usec( usec_t timeout) { int msec; -#if 0 + /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. */ + +#if HAVE_EPOLL_PWAIT2 static bool epoll_pwait2_absent = false; int r; - /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. - * - * FIXME: this is temporarily disabled until epoll_pwait2() becomes more widely available. - * See https://github.com/systemd/systemd/pull/18973 and - * https://github.com/systemd/systemd/issues/19052. */ + /* epoll_pwait2() was added to Linux 5.11 (2021-02-14) and to glibc in 2.35 (2022-02-03). In contrast + * to other syscalls we don't bother with our own fallback syscall wrappers on old libcs, since this + * is not that obvious to implement given the libc and kernel definitions differ in the last + * argument. Moreover, the only reason to use it is the more accurate time-outs (which is not a + * biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's + * missing. */ if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { r = epoll_pwait2(fd, @@ -4089,6 +4651,10 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t r = process_pidfd(e, s, e->event_queue[i].events); break; + case SOURCE_MEMORY_PRESSURE: + r = process_memory_pressure(s, e->event_queue[i].events); + break; + default: assert_not_reached(); } @@ -4132,7 +4698,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_ARMED, -EBUSY); @@ -4235,7 +4801,7 @@ _public_ int sd_event_dispatch(sd_event *e) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_PENDING, -EBUSY); @@ -4275,7 +4841,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -4323,9 +4889,10 @@ _public_ int sd_event_loop(sd_event *e) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); + PROTECT_EVENT(e); while (e->state != SD_EVENT_FINISHED) { @@ -4340,7 +4907,7 @@ _public_ int sd_event_loop(sd_event *e) { _public_ int sd_event_get_fd(sd_event *e) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); return e->epoll_fd; } @@ -4348,7 +4915,7 @@ _public_ int sd_event_get_fd(sd_event *e) { _public_ int sd_event_get_state(sd_event *e) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); return e->state; } @@ -4357,7 +4924,7 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(code, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!e->exit_requested) return -ENODATA; @@ -4370,7 +4937,7 @@ _public_ int sd_event_exit(sd_event *e, int code) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); e->exit_requested = true; e->exit_code = code; @@ -4382,7 +4949,7 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(usec, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) return -EOPNOTSUPP; @@ -4426,7 +4993,7 @@ _public_ int sd_event_get_tid(sd_event *e, pid_t *tid) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(tid, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (e->tid != 0) { *tid = e->tid; @@ -4441,7 +5008,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); if (e->watchdog == !!b) return e->watchdog; @@ -4491,7 +5058,7 @@ fail: _public_ int sd_event_get_watchdog(sd_event *e) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); return e->watchdog; } @@ -4499,7 +5066,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) { _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); + assert_return(!event_origin_changed(e), -ECHILD); *ret = e->iteration; return 0; @@ -4507,6 +5074,8 @@ _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { _public_ int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback) { assert_return(s, -EINVAL); + assert_return(s->event, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); s->destroy_callback = callback; return 0; @@ -4514,6 +5083,7 @@ _public_ int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_d _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret) { assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); if (ret) *ret = s->destroy_callback; @@ -4523,6 +5093,7 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d _public_ int sd_event_source_get_floating(sd_event_source *s) { assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->floating; } @@ -4530,6 +5101,7 @@ _public_ int sd_event_source_get_floating(sd_event_source *s) { _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->floating == !!b) return 0; @@ -4554,6 +5126,7 @@ _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { _public_ int sd_event_source_get_exit_on_failure(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type != SOURCE_EXIT, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); return s->exit_on_failure; } @@ -4561,6 +5134,7 @@ _public_ int sd_event_source_get_exit_on_failure(sd_event_source *s) { _public_ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b) { assert_return(s, -EINVAL); assert_return(s->type != SOURCE_EXIT, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); if (s->exit_on_failure == !!b) return 0; @@ -4573,6 +5147,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval int r; assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); /* Turning on ratelimiting on event source types that don't support it, is a loggable offense. Doing * so is a programming error. */ @@ -4590,6 +5165,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval _public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) { assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); s->ratelimit_expire_callback = callback; return 0; @@ -4597,6 +5173,7 @@ _public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, s _public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) { assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); /* Querying whether an event source has ratelimiting configured is not a loggable offense, hence * don't use assert_return(). Unlike turning on ratelimiting it's not really a programming error. */ @@ -4616,6 +5193,7 @@ _public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_int _public_ int sd_event_source_is_ratelimited(sd_event_source *s) { assert_return(s, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type)) return false; @@ -4626,6 +5204,27 @@ _public_ int sd_event_source_is_ratelimited(sd_event_source *s) { return s->ratelimited; } +_public_ int sd_event_source_leave_ratelimit(sd_event_source *s) { + int r; + + assert_return(s, -EINVAL); + + if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type)) + return 0; + + if (!ratelimit_configured(&s->rate_limit)) + return 0; + + if (!s->ratelimited) + return 0; + + r = event_source_leave_ratelimit(s, /* run_callback */ false); + if (r < 0) + return r; + + return 1; /* tell caller that we indeed just left the ratelimit state */ +} + _public_ int sd_event_set_signal_exit(sd_event *e, int b) { bool change = false; int r; @@ -4677,4 +5276,94 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) { return change; } + +_public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty) { + _cleanup_free_ char *b = NULL; + _cleanup_free_ void *w = NULL; + + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_MEMORY_PRESSURE, -EDOM); + assert_return(ty, -EINVAL); + assert_return(!event_origin_changed(s->event), -ECHILD); + + if (!STR_IN_SET(ty, "some", "full")) + return -EINVAL; + + if (s->memory_pressure.locked) /* Refuse adjusting parameters, if caller told us how to watch for events */ + return -EBUSY; + + char* space = memchr(s->memory_pressure.write_buffer, ' ', s->memory_pressure.write_buffer_size); + if (!space) + return -EINVAL; + + size_t l = (char*) space - (char*) s->memory_pressure.write_buffer; + b = memdup_suffix0(s->memory_pressure.write_buffer, l); + if (!b) + return -ENOMEM; + if (!STR_IN_SET(b, "some", "full")) + return -EINVAL; + + if (streq(b, ty)) + return 0; + + size_t nl = strlen(ty) + (s->memory_pressure.write_buffer_size - l); + w = new(char, nl); + if (!w) + return -ENOMEM; + + memcpy(stpcpy(w, ty), space, (s->memory_pressure.write_buffer_size - l)); + + free_and_replace(s->memory_pressure.write_buffer, w); + s->memory_pressure.write_buffer_size = nl; + s->memory_pressure.locked = false; + + return 1; +} + +_public_ int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec) { + _cleanup_free_ char *b = NULL; + _cleanup_free_ void *w = NULL; + + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_MEMORY_PRESSURE, -EDOM); + assert_return(!event_origin_changed(s->event), -ECHILD); + + if (threshold_usec <= 0 || threshold_usec >= UINT64_MAX) + return -ERANGE; + if (window_usec <= 0 || window_usec >= UINT64_MAX) + return -ERANGE; + if (threshold_usec > window_usec) + return -EINVAL; + + if (s->memory_pressure.locked) /* Refuse adjusting parameters, if caller told us how to watch for events */ + return -EBUSY; + + char* space = memchr(s->memory_pressure.write_buffer, ' ', s->memory_pressure.write_buffer_size); + if (!space) + return -EINVAL; + + size_t l = (char*) space - (char*) s->memory_pressure.write_buffer; + b = memdup_suffix0(s->memory_pressure.write_buffer, l); + if (!b) + return -ENOMEM; + if (!STR_IN_SET(b, "some", "full")) + return -EINVAL; + + if (asprintf((char**) &w, + "%s " USEC_FMT " " USEC_FMT "", + b, + threshold_usec, + window_usec) < 0) + return -EINVAL; + + l = strlen(w) + 1; + if (memcmp_nn(s->memory_pressure.write_buffer, s->memory_pressure.write_buffer_size, w, l) == 0) + return 0; + + free_and_replace(s->memory_pressure.write_buffer, w); + s->memory_pressure.write_buffer_size = l; + s->memory_pressure.locked = false; + + return 1; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c index 66247fd1..c20f7325 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c @@ -7,6 +7,7 @@ #include <unistd.h> #include "fd-util.h" +#include "fs-util.h" #include "hexdecoct.h" #include "id128-util.h" #include "io-util.h" @@ -16,60 +17,51 @@ #if 0 /* NM_IGNORED */ bool id128_is_valid(const char *s) { - size_t i, l; + size_t l; assert(s); l = strlen(s); - if (l == 32) { + if (l == SD_ID128_STRING_MAX - 1) /* Plain formatted 128bit hex string */ + return in_charset(s, HEXDIGITS); - for (i = 0; i < l; i++) { - char c = s[i]; - - if (!ascii_isdigit(c) && - !(c >= 'a' && c <= 'f') && - !(c >= 'A' && c <= 'F')) - return false; - } - - } else if (l == 36) { - + if (l == SD_ID128_UUID_STRING_MAX - 1) { /* Formatted UUID */ - - for (i = 0; i < l; i++) { + for (size_t i = 0; i < l; i++) { char c = s[i]; if (IN_SET(i, 8, 13, 18, 23)) { if (c != '-') return false; - } else { - if (!ascii_isdigit(c) && - !(c >= 'a' && c <= 'f') && - !(c >= 'A' && c <= 'F')) - return false; - } + } else if (!ascii_ishex(c)) + return false; } + return true; + } - } else - return false; - - return true; + return false; } #endif /* NM_IGNORED */ -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { - char buffer[36 + 2]; +int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) { + char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ + sd_id128_t id; ssize_t l; + int r; assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you - * accept". */ + * accept". + * + * This returns the following: + * -ENOMEDIUM: an empty string, + * -ENOPKG: "uninitialized" or "uninitialized\n", + * -EUCLEAN: other invalid strings. */ l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ if (l < 0) @@ -79,76 +71,91 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { switch (l) { - case 13: - case 14: - /* Treat an "uninitialized" id file like an empty one */ - return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL; + case STRLEN("uninitialized"): + case STRLEN("uninitialized\n"): + return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL; - case 33: /* plain UUID with trailing newline */ - if (buffer[32] != '\n') - return -EINVAL; + case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */ + if (buffer[SD_ID128_STRING_MAX-1] != '\n') + return -EUCLEAN; _fallthrough_; - case 32: /* plain UUID without trailing newline */ - if (f == ID128_UUID) - return -EINVAL; + case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */ + if (!FLAGS_SET(f, ID128_FORMAT_PLAIN)) + return -EUCLEAN; - buffer[32] = 0; + buffer[SD_ID128_STRING_MAX-1] = 0; break; - case 37: /* RFC UUID with trailing newline */ - if (buffer[36] != '\n') - return -EINVAL; + case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */ + if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n') + return -EUCLEAN; _fallthrough_; - case 36: /* RFC UUID without trailing newline */ - if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT)) - return -EINVAL; + case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */ + if (!FLAGS_SET(f, ID128_FORMAT_UUID)) + return -EUCLEAN; - buffer[36] = 0; + buffer[SD_ID128_UUID_STRING_MAX-1] = 0; break; default: - return -EINVAL; + return -EUCLEAN; } - return sd_id128_from_string(buffer, ret); + r = sd_id128_from_string(buffer, &id); + if (r == -EINVAL) + return -EUCLEAN; + if (r < 0) + return r; + + if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) + return -ENOMEDIUM; + + if (ret) + *ret = id; + return 0; } -int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { - _cleanup_close_ int fd = -1; +int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) { + _cleanup_close_ int fd = -EBADF; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); - fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); + fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0); if (fd < 0) - return -errno; + return fd; return id128_read_fd(fd, f, ret); } #if 0 /* NM_IGNORED */ -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { - char buffer[36 + 2]; +int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) { + char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ size_t sz; int r; assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); + assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID)); - if (f != ID128_UUID) { + if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) + return -ENOMEDIUM; + + if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) { assert_se(sd_id128_to_string(id, buffer)); - buffer[SD_ID128_STRING_MAX - 1] = '\n'; sz = SD_ID128_STRING_MAX; } else { assert_se(sd_id128_to_uuid_string(id, buffer)); - buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n'; sz = SD_ID128_UUID_STRING_MAX; } + buffer[sz - 1] = '\n'; r = loop_write(fd, buffer, sz, false); if (r < 0) return r; - if (do_sync) { + if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) { r = fsync_full(fd); if (r < 0) return r; @@ -157,14 +164,17 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { return 0; } -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { - _cleanup_close_ int fd = -1; +int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) { + _cleanup_close_ int fd = -EBADF; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); - fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444); + fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444); if (fd < 0) - return -errno; + return fd; - return id128_write_fd(fd, f, id, do_sync); + return id128_write_fd(fd, f, id); } void id128_hash_func(const sd_id128_t *p, struct siphash *state) { @@ -189,6 +199,7 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) { } DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free); int id128_get_product(sd_id128_t *ret) { sd_id128_t uuid; @@ -199,9 +210,9 @@ int id128_get_product(sd_id128_t *ret) { /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ - r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid); if (r == -ENOENT) - r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid); + r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid); if (r < 0) return r; diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h index 17b180c1..7bcbd8e5 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <fcntl.h> #include <stdbool.h> #include "sd-id128.h" @@ -10,27 +11,42 @@ bool id128_is_valid(const char *s) _pure_; -typedef enum Id128Format { - ID128_ANY, - ID128_PLAIN, /* formatted as 32 hex chars as-is */ - ID128_PLAIN_OR_UNINIT, /* formatted as 32 hex chars as-is; allow special "uninitialized" - * value when reading from file (id128_read() and id128_read_fd()). - * - * This format should be used when reading a machine-id file. */ - ID128_UUID, /* formatted as 36 character uuid string */ - _ID128_FORMAT_MAX, -} Id128Format; +typedef enum Id128Flag { + ID128_FORMAT_PLAIN = 1 << 0, /* formatted as 32 hex chars as-is */ + ID128_FORMAT_UUID = 1 << 1, /* formatted as 36 character uuid string */ + ID128_FORMAT_ANY = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID, -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret); -int id128_read(const char *p, Id128Format f, sd_id128_t *ret); + ID128_SYNC_ON_WRITE = 1 << 2, /* Sync the file after write. Used only when writing an ID. */ + ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */ +} Id128Flag; -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); +int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret); +int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret); +static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) { + return id128_read_at(AT_FDCWD, path, f, ret); +} + +int id128_write_fd(int fd, Id128Flag f, sd_id128_t id); +int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id); +static inline int id128_write(const char *path, Id128Flag f, sd_id128_t id) { + return id128_write_at(AT_FDCWD, path, f, id); +} + +int id128_get_machine(const char *root, sd_id128_t *ret); +int id128_get_machine_at(int rfd, sd_id128_t *ret); void id128_hash_func(const sd_id128_t *p, struct siphash *state); int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; +extern const struct hash_ops id128_hash_ops_free; sd_id128_t id128_make_v4_uuid(sd_id128_t id); int id128_get_product(sd_id128_t *ret); + +/* A helper to check for the three relevant cases of "machine ID not initialized" */ +#define ERRNO_IS_MACHINE_ID_UNSET(r) \ + IN_SET(abs(r), \ + ENOENT, \ + ENOMEDIUM, \ + ENOPKG) diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c index b7d7788c..c63e1a97 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c @@ -9,6 +9,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "chase.h" #include "fd-util.h" #include "hexdecoct.h" #include "hmac.h" @@ -16,20 +17,25 @@ #include "io-util.h" #include "macro.h" #include "missing_syscall.h" +#include "missing_threads.h" +#include "path-util.h" #include "random-util.h" +#include "stat-util.h" #include "user-util.h" -#include "util.h" #if 0 /* NM_IGNORED */ _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { + size_t k = 0; + assert_return(s, NULL); - for (size_t n = 0; n < 16; n++) { - s[n*2] = hexchar(id.bytes[n] >> 4); - s[n*2+1] = hexchar(id.bytes[n] & 0xF); + for (size_t n = 0; n < sizeof(sd_id128_t); n++) { + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); } - s[SD_ID128_STRING_MAX-1] = 0; + assert(k == SD_ID128_STRING_MAX - 1); + s[k] = 0; return s; } @@ -41,7 +47,7 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - for (size_t n = 0; n < 16; n++) { + for (size_t n = 0; n < sizeof(sd_id128_t); n++) { if (IN_SET(n, 4, 6, 8, 10)) s[k++] = '-'; @@ -57,14 +63,14 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD } #endif /* NM_IGNORED */ -_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { - unsigned n, i; +_public_ int sd_id128_from_string(const char *s, sd_id128_t *ret) { + size_t n, i; sd_id128_t t; bool is_guid = false; assert_return(s, -EINVAL); - for (n = 0, i = 0; n < 16;) { + for (n = 0, i = 0; n < sizeof(sd_id128_t);) { int a, b; if (s[i] == '-') { @@ -94,7 +100,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { t.bytes[n++] = (a << 4) | b; } - if (i != (is_guid ? 36 : 32)) + if (i != (is_guid ? SD_ID128_UUID_STRING_MAX : SD_ID128_STRING_MAX) - 1) return -EINVAL; if (s[i] != 0) @@ -125,34 +131,65 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { static thread_local sd_id128_t saved_machine_id = {}; int r; - assert_return(ret, -EINVAL); - if (sd_id128_is_null(saved_machine_id)) { - r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id); + r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, &saved_machine_id); if (r < 0) return r; - - if (sd_id128_is_null(saved_machine_id)) - return -ENOMEDIUM; } - *ret = saved_machine_id; + if (ret) + *ret = saved_machine_id; return 0; } +#if 0 /* NM_IGNORED */ +int id128_get_machine_at(int rfd, sd_id128_t *ret) { + _cleanup_close_ int fd = -EBADF; + int r; + + assert(rfd >= 0 || rfd == AT_FDCWD); + + r = dir_fd_is_root_or_cwd(rfd); + if (r < 0) + return r; + if (r > 0) + return sd_id128_get_machine(ret); + + fd = chase_and_openat(rfd, "/etc/machine-id", CHASE_AT_RESOLVE_IN_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); + if (fd < 0) + return fd; + + return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret); +} + +int id128_get_machine(const char *root, sd_id128_t *ret) { + _cleanup_close_ int fd = -EBADF; + + if (empty_or_root(root)) + return sd_id128_get_machine(ret); + + fd = chase_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); + if (fd < 0) + return fd; + + return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret); +} +#endif /* NM_IGNORED */ + _public_ int sd_id128_get_boot(sd_id128_t *ret) { static thread_local sd_id128_t saved_boot_id = {}; int r; - assert_return(ret, -EINVAL); - if (sd_id128_is_null(saved_boot_id)) { - r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id); + r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id); + if (r == -ENOENT && proc_mounted() == 0) + return -ENOSYS; if (r < 0) return r; } - *ret = saved_boot_id; + if (ret) + *ret = saved_boot_id; return 0; } @@ -203,22 +240,22 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { /* Chop off the final description string */ d = strrchr(description, ';'); if (!d) - return -EIO; + return -EUCLEAN; *d = 0; /* Look for the permissions */ p = strrchr(description, ';'); if (!p) - return -EIO; + return -EUCLEAN; errno = 0; perms = strtoul(p + 1, &e, 16); if (errno > 0) return -errno; if (e == p + 1) /* Read at least one character */ - return -EIO; + return -EUCLEAN; if (e != d) /* Must reached the end */ - return -EIO; + return -EUCLEAN; if ((perms & ~MAX_PERMS) != 0) return -EPERM; @@ -228,7 +265,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { /* Look for the group ID */ g = strrchr(description, ';'); if (!g) - return -EIO; + return -EUCLEAN; r = parse_gid(g + 1, &gid); if (r < 0) return r; @@ -239,7 +276,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { /* Look for the user ID */ u = strrchr(description, ';'); if (!u) - return -EIO; + return -EUCLEAN; r = parse_uid(u + 1, &uid); if (r < 0) return r; @@ -250,13 +287,14 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { if (c < 0) return -errno; if (c != sizeof(sd_id128_t)) - return -EIO; + return -EUCLEAN; return 0; } static int get_invocation_from_environment(sd_id128_t *ret) { const char *e; + int r; assert(ret); @@ -264,33 +302,31 @@ static int get_invocation_from_environment(sd_id128_t *ret) { if (!e) return -ENXIO; - return sd_id128_from_string(e, ret); + r = sd_id128_from_string(e, ret); + return r == -EINVAL ? -EUCLEAN : r; } _public_ int sd_id128_get_invocation(sd_id128_t *ret) { static thread_local sd_id128_t saved_invocation_id = {}; int r; - assert_return(ret, -EINVAL); - if (sd_id128_is_null(saved_invocation_id)) { /* We first check the environment. The environment variable is primarily relevant for user * services, and sufficiently safe as long as no privilege boundary is involved. */ r = get_invocation_from_environment(&saved_invocation_id); - if (r >= 0) { - *ret = saved_invocation_id; - return 0; - } else if (r != -ENXIO) - return r; - - /* The kernel keyring is relevant for system services (as for user services we don't store - * the invocation ID in the keyring, as there'd be no trust benefit in that). */ - r = get_invocation_from_keyring(&saved_invocation_id); + if (r == -ENXIO) + /* The kernel keyring is relevant for system services (as for user services we don't + * store the invocation ID in the keyring, as there'd be no trust benefit in that). */ + r = get_invocation_from_keyring(&saved_invocation_id); if (r < 0) return r; + + if (sd_id128_is_null(saved_invocation_id)) + return -ENOMEDIUM; } - *ret = saved_invocation_id; + if (ret) + *ret = saved_invocation_id; return 0; } diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h new file mode 100644 index 00000000..e3d647f7 --- /dev/null +++ b/src/libnm-systemd-core/src/systemd/sd-device.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddevicehfoo +#define foosddevicehfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <https://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + +#include "sd-event.h" +#include "sd-id128.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_device sd_device; +typedef struct sd_device_enumerator sd_device_enumerator; +typedef struct sd_device_monitor sd_device_monitor; + +__extension__ typedef enum sd_device_action_t { + SD_DEVICE_ADD, + SD_DEVICE_REMOVE, + SD_DEVICE_CHANGE, + SD_DEVICE_MOVE, + SD_DEVICE_ONLINE, + SD_DEVICE_OFFLINE, + SD_DEVICE_BIND, + SD_DEVICE_UNBIND, + _SD_DEVICE_ACTION_MAX, + _SD_DEVICE_ACTION_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(DEVICE_ACTION) +} sd_device_action_t; + +/* callback */ + +typedef int (*sd_device_monitor_handler_t)(sd_device_monitor *m, sd_device *device, void *userdata); + +/* device */ + +sd_device *sd_device_ref(sd_device *device); +sd_device *sd_device_unref(sd_device *device); + +int sd_device_new_from_syspath(sd_device **ret, const char *syspath); +int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); +int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); +int sd_device_new_from_device_id(sd_device **ret, const char *id); +int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st); +int sd_device_new_from_devname(sd_device **ret, const char *devname); +int sd_device_new_from_path(sd_device **ret, const char *path); +int sd_device_new_from_ifname(sd_device **ret, const char *ifname); +int sd_device_new_from_ifindex(sd_device **ret, int ifindex); + +int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix); + +int sd_device_get_parent(sd_device *child, sd_device **ret); +int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); + +int sd_device_get_syspath(sd_device *device, const char **ret); +int sd_device_get_subsystem(sd_device *device, const char **ret); +int sd_device_get_devtype(sd_device *device, const char **ret); +int sd_device_get_devnum(sd_device *device, dev_t *devnum); +int sd_device_get_ifindex(sd_device *device, int *ifindex); +int sd_device_get_driver(sd_device *device, const char **ret); +int sd_device_get_devpath(sd_device *device, const char **ret); +int sd_device_get_devname(sd_device *device, const char **ret); +int sd_device_get_sysname(sd_device *device, const char **ret); +int sd_device_get_sysnum(sd_device *device, const char **ret); +int sd_device_get_action(sd_device *device, sd_device_action_t *ret); +int sd_device_get_seqnum(sd_device *device, uint64_t *ret); +int sd_device_get_diskseq(sd_device *device, uint64_t *ret); + +int sd_device_get_is_initialized(sd_device *device); +int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret); +int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret); + +const char *sd_device_get_tag_first(sd_device *device); +const char *sd_device_get_tag_next(sd_device *device); +const char *sd_device_get_current_tag_first(sd_device *device); +const char *sd_device_get_current_tag_next(sd_device *device); +const char *sd_device_get_devlink_first(sd_device *device); +const char *sd_device_get_devlink_next(sd_device *device); +const char *sd_device_get_property_first(sd_device *device, const char **value); +const char *sd_device_get_property_next(sd_device *device, const char **value); +const char *sd_device_get_sysattr_first(sd_device *device); +const char *sd_device_get_sysattr_next(sd_device *device); +sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix); +sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix); + +int sd_device_has_tag(sd_device *device, const char *tag); +int sd_device_has_current_tag(sd_device *device, const char *tag); +int sd_device_get_property_value(sd_device *device, const char *key, const char **value); +int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret); +int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); + +int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value); +int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4); +int sd_device_trigger(sd_device *device, sd_device_action_t action); +int sd_device_trigger_with_uuid(sd_device *device, sd_device_action_t action, sd_id128_t *ret_uuid); +int sd_device_open(sd_device *device, int flags); + +/* device enumerator */ + +int sd_device_enumerator_new(sd_device_enumerator **ret); +sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); +sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); + +sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator); + +int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); +int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); +int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); +int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); +int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); + +/* device monitor */ + +int sd_device_monitor_new(sd_device_monitor **ret); +sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m); +sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m); + +int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size); +int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event); +int sd_device_monitor_detach_event(sd_device_monitor *m); +sd_event *sd_device_monitor_get_event(sd_device_monitor *m); +sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m); +int sd_device_monitor_set_description(sd_device_monitor *m, const char *description); +int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret); +int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata); +int sd_device_monitor_stop(sd_device_monitor *m); + +int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype); +int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag); +int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, const char *sysattr, const char *value, int match); +int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match); +int sd_device_monitor_filter_update(sd_device_monitor *m); +int sd_device_monitor_filter_remove(sd_device_monitor *m); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_monitor, sd_device_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h index 2c66c51b..a9fa7856 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h @@ -23,6 +23,7 @@ #include <net/ethernet.h> #include <sys/types.h> +#include "sd-device.h" #include "sd-dhcp6-lease.h" #include "sd-dhcp6-option.h" #include "sd-event.h" @@ -263,6 +264,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable); +int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, @@ -279,6 +281,7 @@ int sd_dhcp6_client_attach_event( int64_t priority); int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client); +int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev); sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client); sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client); int sd_dhcp6_client_new(sd_dhcp6_client **ret); diff --git a/src/libnm-systemd-core/src/systemd/sd-event.h b/src/libnm-systemd-core/src/systemd/sd-event.h index cae4c867..49d69759 100644 --- a/src/libnm-systemd-core/src/systemd/sd-event.h +++ b/src/libnm-systemd-core/src/systemd/sd-event.h @@ -99,6 +99,7 @@ int sd_event_add_inotify_fd(sd_event *e, sd_event_source **s, int fd, uint32_t m int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_memory_pressure(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_prepare(sd_event *e); int sd_event_wait(sd_event *e, uint64_t usec); @@ -160,6 +161,8 @@ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags); #endif int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); +int sd_event_source_set_memory_pressure_type(sd_event_source *e, const char *ty); +int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec); int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); int sd_event_source_get_floating(sd_event_source *s); @@ -170,6 +173,9 @@ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, un int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst); int sd_event_source_is_ratelimited(sd_event_source *s); int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback); +int sd_event_source_leave_ratelimit(sd_event_source *s); + +int sd_event_trim_memory(void); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); |