summary refs log tree commit diff
path: root/src/libnm-systemd-core
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-core')
-rw-r--r--src/libnm-systemd-core/meson.build1
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c15
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h10
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/sd-device.h3
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/sd-messages.h (renamed from src/libnm-systemd-core/sd-adapt-core/device-util.h)0
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c38
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h4
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h5
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c25
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c107
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c6
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h103
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h12
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c4
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c921
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c145
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h44
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c118
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-device.h167
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h3
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-event.h6
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);