diff options
Diffstat (limited to 'src/oom')
-rw-r--r-- | src/oom/oomd-manager.c | 209 | ||||
-rw-r--r-- | src/oom/oomd-manager.h | 13 | ||||
-rw-r--r-- | src/oom/oomd.c | 9 |
3 files changed, 167 insertions, 64 deletions
diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c index 1f38f7eac1..2684cfd85a 100644 --- a/src/oom/oomd-manager.c +++ b/src/oom/oomd-manager.c @@ -1,28 +1,31 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-daemon.h" + #include "bus-log-control-api.h" #include "bus-util.h" #include "bus-polkit.h" #include "cgroup-util.h" #include "fd-util.h" #include "fileio.h" +#include "format-util.h" #include "memory-util.h" #include "oomd-manager-bus.h" #include "oomd-manager.h" #include "path-util.h" #include "percent-util.h" -typedef struct ManagedOOMReply { +typedef struct ManagedOOMMessage { ManagedOOMMode mode; char *path; char *property; uint32_t limit; -} ManagedOOMReply; +} ManagedOOMMessage; -static void managed_oom_reply_destroy(ManagedOOMReply *reply) { - assert(reply); - free(reply->path); - free(reply->property); +static void managed_oom_message_destroy(ManagedOOMMessage *message) { + assert(message); + free(message->path); + free(message->property); } static int managed_oom_mode(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { @@ -40,96 +43,142 @@ static int managed_oom_mode(const char *name, JsonVariant *v, JsonDispatchFlags return 0; } -static int process_managed_oom_reply( - Varlink *link, - JsonVariant *parameters, - const char *error_id, - VarlinkReplyFlags flags, - void *userdata) { +static int process_managed_oom_message(Manager *m, uid_t uid, JsonVariant *parameters) { JsonVariant *c, *cgroups; - Manager *m = userdata; - int r = 0; - - assert(m); + int r; static const JsonDispatch dispatch_table[] = { - { "mode", JSON_VARIANT_STRING, managed_oom_mode, offsetof(ManagedOOMReply, mode), JSON_MANDATORY }, - { "path", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMReply, path), JSON_MANDATORY }, - { "property", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMReply, property), JSON_MANDATORY }, - { "limit", JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(ManagedOOMReply, limit), 0 }, + { "mode", JSON_VARIANT_STRING, managed_oom_mode, offsetof(ManagedOOMMessage, mode), JSON_MANDATORY }, + { "path", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMMessage, path), JSON_MANDATORY }, + { "property", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMMessage, property), JSON_MANDATORY }, + { "limit", JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(ManagedOOMMessage, limit), 0 }, {}, }; - if (error_id) { - r = -EIO; - log_debug("Error getting ManagedOOM cgroups: %s", error_id); - goto finish; - } + assert(m); + assert(parameters); cgroups = json_variant_by_key(parameters, "cgroups"); - if (!cgroups) { - r = -EINVAL; - goto finish; - } + if (!cgroups) + return -EINVAL; /* Skip malformed elements and keep processing in case the others are good */ JSON_VARIANT_ARRAY_FOREACH(c, cgroups) { - _cleanup_(managed_oom_reply_destroy) ManagedOOMReply reply = {}; + _cleanup_(managed_oom_message_destroy) ManagedOOMMessage message = {}; OomdCGroupContext *ctx; Hashmap *monitor_hm; loadavg_t limit; - int ret; if (!json_variant_is_object(c)) continue; - ret = json_dispatch(c, dispatch_table, NULL, 0, &reply); - if (ret == -ENOMEM) { - r = ret; - goto finish; - } - if (ret < 0) + r = json_dispatch(c, dispatch_table, NULL, 0, &message); + if (r == -ENOMEM) + return r; + if (r < 0) continue; - monitor_hm = streq(reply.property, "ManagedOOMSwap") ? + if (uid != 0) { + uid_t cg_uid; + + r = cg_path_get_owner_uid(message.path, &cg_uid); + if (r < 0) { + log_debug("Failed to get cgroup %s owner uid: %m", message.path); + continue; + } + + /* Let's not be lenient for permission errors and skip processing if we receive an + * update for a cgroup that doesn't belong to the user. */ + if (uid != cg_uid) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), + "cgroup path owner UID does not match sender uid " + "(" UID_FMT " != " UID_FMT ")", uid, cg_uid); + } + + monitor_hm = streq(message.property, "ManagedOOMSwap") ? m->monitored_swap_cgroup_contexts : m->monitored_mem_pressure_cgroup_contexts; - if (reply.mode == MANAGED_OOM_AUTO) { - (void) oomd_cgroup_context_free(hashmap_remove(monitor_hm, empty_to_root(reply.path))); + if (message.mode == MANAGED_OOM_AUTO) { + (void) oomd_cgroup_context_free(hashmap_remove(monitor_hm, empty_to_root(message.path))); continue; } limit = m->default_mem_pressure_limit; - if (streq(reply.property, "ManagedOOMMemoryPressure") && reply.limit > 0) { - int permyriad = UINT32_SCALE_TO_PERMYRIAD(reply.limit); + if (streq(message.property, "ManagedOOMMemoryPressure") && message.limit > 0) { + int permyriad = UINT32_SCALE_TO_PERMYRIAD(message.limit); - ret = store_loadavg_fixed_point( + r = store_loadavg_fixed_point( (unsigned long) permyriad / 100, (unsigned long) permyriad % 100, &limit); - if (ret < 0) + if (r < 0) continue; } - ret = oomd_insert_cgroup_context(NULL, monitor_hm, reply.path); - if (ret == -ENOMEM) { - r = ret; - goto finish; - } - if (ret < 0 && ret != -EEXIST) - log_debug_errno(ret, "Failed to insert reply, ignoring: %m"); + r = oomd_insert_cgroup_context(NULL, monitor_hm, message.path); + if (r == -ENOMEM) + return r; + if (r < 0 && r != -EEXIST) + log_debug_errno(r, "Failed to insert message, ignoring: %m"); /* Always update the limit in case it was changed. For non-memory pressure detection the value is * ignored so always updating it here is not a problem. */ - ctx = hashmap_get(monitor_hm, empty_to_root(reply.path)); + ctx = hashmap_get(monitor_hm, empty_to_root(message.path)); if (ctx) ctx->mem_pressure_limit = limit; } + return 0; +} + +static int process_managed_oom_request( + Varlink *link, + JsonVariant *parameters, + VarlinkMethodFlags flags, + void *userdata) { + Manager *m = userdata; + uid_t uid; + int r; + + assert(m); + + r = varlink_get_peer_uid(link, &uid); + if (r < 0) + return log_error_errno(r, "Failed to get varlink peer uid: %m"); + + return process_managed_oom_message(m, uid, parameters); +} + +static int process_managed_oom_reply( + Varlink *link, + JsonVariant *parameters, + const char *error_id, + VarlinkReplyFlags flags, + void *userdata) { + Manager *m = userdata; + uid_t uid; + int r; + + assert(m); + + if (error_id) { + r = -EIO; + log_debug("Error getting ManagedOOM cgroups: %s", error_id); + goto finish; + } + + r = varlink_get_peer_uid(link, &uid); + if (r < 0) { + log_error_errno(r, "Failed to get varlink peer uid: %m"); + goto finish; + } + + r = process_managed_oom_message(m, uid, parameters); + finish: if (!FLAGS_SET(flags, VARLINK_REPLY_CONTINUES)) - m->varlink = varlink_close_unref(link); + m->varlink_client = varlink_close_unref(link); return r; } @@ -275,9 +324,9 @@ static int acquire_managed_oom_connect(Manager *m) { assert(m); assert(m->event); - r = varlink_connect_address(&link, VARLINK_ADDR_PATH_MANAGED_OOM); + r = varlink_connect_address(&link, VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM); if (r < 0) - return log_error_errno(r, "Failed to connect to %s: %m", VARLINK_ADDR_PATH_MANAGED_OOM); + return log_error_errno(r, "Failed to connect to " VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM ": %m"); (void) varlink_set_userdata(link, m); (void) varlink_set_description(link, "oomd"); @@ -295,7 +344,7 @@ static int acquire_managed_oom_connect(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to observe varlink call: %m"); - m->varlink = TAKE_PTR(link); + m->varlink_client = TAKE_PTR(link); return 0; } @@ -317,7 +366,7 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void return log_error_errno(r, "Failed to set relative time for timer: %m"); /* Reconnect if our connection dropped */ - if (!m->varlink) { + if (!m->varlink_client) { r = acquire_managed_oom_connect(m); if (r < 0) return log_error_errno(r, "Failed to acquire varlink connection: %m"); @@ -407,7 +456,7 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t return log_error_errno(r, "Failed to set relative time for timer: %m"); /* Reconnect if our connection dropped */ - if (!m->varlink) { + if (!m->varlink_client) { r = acquire_managed_oom_connect(m); if (r < 0) return log_error_errno(r, "Failed to acquire varlink connection: %m"); @@ -564,7 +613,8 @@ static int monitor_memory_pressure_contexts(Manager *m) { Manager* manager_free(Manager *m) { assert(m); - varlink_close_unref(m->varlink); + varlink_server_unref(m->varlink_server); + varlink_close_unref(m->varlink_client); sd_event_source_unref(m->swap_context_event_source); sd_event_source_unref(m->mem_pressure_context_event_source); sd_event_unref(m->event); @@ -648,12 +698,47 @@ static int manager_connect_bus(Manager *m) { return 0; } +static int manager_varlink_init(Manager *m, int fd) { + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + int r; + + assert(m); + assert(!m->varlink_server); + + r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA); + if (r < 0) + return log_error_errno(r, "Failed to allocate varlink server object: %m"); + + varlink_server_set_userdata(s, m); + + r = varlink_server_bind_method(s, "io.systemd.oom.ReportManagedOOMCGroups", process_managed_oom_request); + if (r < 0) + return log_error_errno(r, "Failed to register varlink method: %m"); + + if (fd < 0) + r = varlink_server_listen_address(s, VARLINK_ADDR_PATH_MANAGED_OOM_USER, 0666); + else + r = varlink_server_listen_fd(s, fd); + if (r < 0) + return log_error_errno(r, "Failed to bind to varlink socket: %m"); + + r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); + + log_debug("Initialized systemd-oomd varlink server"); + + m->varlink_server = TAKE_PTR(s); + return 0; +} + int manager_start( Manager *m, bool dry_run, int swap_used_limit_permyriad, int mem_pressure_limit_permyriad, - usec_t mem_pressure_usec) { + usec_t mem_pressure_usec, + int fd) { unsigned long l, f; int r; @@ -688,6 +773,10 @@ int manager_start( if (r < 0) return r; + r = manager_varlink_init(m, fd); + if (r < 0) + return r; + r = monitor_memory_pressure_contexts(m); if (r < 0) return r; diff --git a/src/oom/oomd-manager.h b/src/oom/oomd-manager.h index dc170f2bda..8f0dd412da 100644 --- a/src/oom/oomd-manager.h +++ b/src/oom/oomd-manager.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "sd-bus.h" +#include "sd-event.h" + #include "conf-parser.h" #include "oomd-util.h" -#include "sd-event.h" #include "varlink.h" /* Polling interval for monitoring stats */ @@ -51,7 +53,12 @@ struct Manager { sd_event_source *swap_context_event_source; sd_event_source *mem_pressure_context_event_source; - Varlink *varlink; + /* This varlink object is used to manage the subscription from systemd-oomd to PID1 which it uses to + * listen for changes in ManagedOOM settings (oomd client - systemd server). */ + Varlink *varlink_client; + /* This varlink server object is used to manage systemd-oomd's varlink server which is used by user + * managers to report changes in ManagedOOM settings (oomd server - systemd client). */ + VarlinkServer *varlink_server; }; Manager* manager_free(Manager *m); @@ -59,7 +66,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); int manager_new(Manager **ret); -int manager_start(Manager *m, bool dry_run, int swap_used_limit_permyriad, int mem_pressure_limit_permyriad, usec_t mem_pressure_usec); +int manager_start(Manager *m, bool dry_run, int swap_used_limit_permyriad, int mem_pressure_limit_permyriad, usec_t mem_pressure_usec, int fd); int manager_get_dump_string(Manager *m, char **ret); diff --git a/src/oom/oomd.c b/src/oom/oomd.c index abc96a0587..603baada5a 100644 --- a/src/oom/oomd.c +++ b/src/oom/oomd.c @@ -136,6 +136,12 @@ static int run(int argc, char *argv[]) { /* Do some basic requirement checks for running systemd-oomd. It's not exhaustive as some of the other * requirements do not have a reliable means to check for in code. */ + int n = sd_listen_fds(0); + if (n > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Received too many file descriptors"); + + int fd = n == 1 ? SD_LISTEN_FDS_START : -1; + /* SwapTotal is always available in /proc/meminfo and defaults to 0, even on swap-disabled kernels. */ r = get_proc_field("/proc/meminfo", "SwapTotal", WHITESPACE, &swap); if (r < 0) @@ -175,7 +181,8 @@ static int run(int argc, char *argv[]) { arg_dry_run, arg_swap_used_limit_permyriad, arg_mem_pressure_limit_permyriad, - arg_mem_pressure_usec); + arg_mem_pressure_usec, + fd); if (r < 0) return log_error_errno(r, "Failed to start up daemon: %m"); |