]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemd-oomd: dbus hook ups and oomctl CLI
authorAnita Zhang <the.anitazha@gmail.com>
Wed, 9 Sep 2020 06:46:27 +0000 (23:46 -0700)
committerAnita Zhang <the.anitazha@gmail.com>
Thu, 8 Oct 2020 00:12:24 +0000 (17:12 -0700)
meson.build
src/oom/meson.build
src/oom/oomctl.c [new file with mode: 0644]
src/oom/oomd-manager-bus.c [new file with mode: 0644]
src/oom/oomd-manager-bus.h [new file with mode: 0644]
src/oom/oomd-manager.c
src/oom/oomd-manager.h
src/oom/oomd-util.c
src/oom/oomd-util.h
src/oom/org.freedesktop.oom1.conf [new file with mode: 0644]

index ecb8426ba6058c3357323743c54a7663e90af788..435c0bbc98e6e9d76f5539b1311551050dd4c9ed 100644 (file)
@@ -2671,6 +2671,16 @@ if conf.get('ENABLE_OOMD') == 1
                    install_rpath : rootlibexecdir,
                    install : true,
                    install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                   'oomctl',
+                   oomctl_sources,
+                   include_directories : includes,
+                   link_with : [libshared],
+                   dependencies : [],
+                   install_rpath : rootlibexecdir,
+                   install : true,
+                   install_dir : rootbindir)
 endif
 
 if conf.get('ENABLE_BINFMT') == 1
index 92c47e04c474d5748fda643ddcc4422fb592b6c8..aa85dab94709ff9c72c95856076e4ab08548d68a 100644 (file)
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 systemd_oomd_sources = files('''
+        oomd-manager-bus.c
+        oomd-manager-bus.h
         oomd-manager.c
         oomd-manager.h
         oomd-util.c
@@ -8,6 +10,10 @@ systemd_oomd_sources = files('''
         oomd.c
 '''.split())
 
+oomctl_sources = files('''
+        oomctl.c
+'''.split())
+
 if conf.get('ENABLE_OOMD') == 1
         tests += [
                 [['src/oom/test-oomd-util.c',
@@ -17,6 +23,9 @@ if conf.get('ENABLE_OOMD') == 1
                  []]
         ]
 
+        install_data('org.freedesktop.oom1.conf',
+                     install_dir : dbuspolicydir)
+
         install_data('oomd.conf',
                      install_dir : pkgsysconfdir)
 endif
diff --git a/src/oom/oomctl.c b/src/oom/oomctl.c
new file mode 100644 (file)
index 0000000..01e43d3
--- /dev/null
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "bus-error.h"
+#include "copy.h"
+#include "main-func.h"
+#include "pretty-print.h"
+#include "terminal-util.h"
+#include "verbs.h"
+
+static PagerFlags arg_pager_flags = 0;
+
+static int help(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        (void) pager_open(arg_pager_flags);
+
+        r = terminal_urlify_man("oomctl", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] COMMAND ...\n\n"
+               "%2$sManage or inspect the userspace OOM killer.%3$s\n"
+               "\n%4$sCommands:%5$s\n"
+               "  dump                        Output the current state of systemd-oomd\n"
+               "\n%4$sOptions:%5$s\n"
+               "  -h --help                   Show this help\n"
+               "     --version                Show package version\n"
+               "     --no-pager               Do not pipe output into a pager\n"
+               "\nSee the %6$s for details.\n"
+               , program_invocation_short_name
+               , ansi_highlight(), ansi_normal()
+               , ansi_underline(), ansi_normal()
+               , link
+        );
+
+        return 0;
+}
+
+static int dump_state(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int fd = -1;
+        int r;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect system bus: %m");
+
+        (void) pager_open(arg_pager_flags);
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.oom1",
+                        "/org/freedesktop/oom1",
+                        "org.freedesktop.oom1.Manager",
+                        "DumpByFileDescriptor",
+                        &error,
+                        &reply,
+                        NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to dump context: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "h", &fd);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        fflush(stdout);
+        return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
+        };
+
+        static const struct option options[] = {
+                { "help",     no_argument, NULL, 'h'          },
+                { "version",  no_argument, NULL, ARG_VERSION  },
+                { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+                {}
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+                switch (c) {
+
+                        case 'h':
+                                return help(0, NULL, NULL);
+
+                        case ARG_VERSION:
+                                return version();
+
+                        case ARG_NO_PAGER:
+                                arg_pager_flags |= PAGER_DISABLE;
+                                break;
+
+                        case '?':
+                                return -EINVAL;
+
+                        default:
+                                assert_not_reached("Invalid option passed.");
+                }
+
+        return 1;
+}
+
+static int run(int argc, char* argv[]) {
+        static const Verb verbs[] = {
+                { "help", VERB_ANY, VERB_ANY, 0,            help       },
+                { "dump", VERB_ANY, 1,        VERB_DEFAULT, dump_state },
+                {}
+        };
+
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/oom/oomd-manager-bus.c b/src/oom/oomd-manager-bus.c
new file mode 100644 (file)
index 0000000..67c5fbf
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/capability.h>
+
+#include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "fd-util.h"
+#include "oomd-manager-bus.h"
+#include "oomd-manager.h"
+#include "user-util.h"
+
+static int bus_method_dump_by_fd(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_free_ char *dump = NULL;
+        _cleanup_close_ int fd = -1;
+        Manager *m = userdata;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = manager_get_dump_string(m, &dump);
+        if (r < 0)
+                return r;
+
+        fd = acquire_data_fd(dump, strlen(dump), 0);
+        if (fd < 0)
+                return fd;
+
+        return sd_bus_reply_method_return(message, "h", fd);
+}
+
+const sd_bus_vtable manager_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+        SD_BUS_METHOD("DumpByFileDescriptor", NULL, "h", bus_method_dump_by_fd, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_VTABLE_END
+};
diff --git a/src/oom/oomd-manager-bus.h b/src/oom/oomd-manager-bus.h
new file mode 100644 (file)
index 0000000..60ccf3b
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+typedef struct Manager Manager;
+
+extern const sd_bus_vtable manager_vtable[];
index b66ac85cc0b34eb3bfa3aee3a4fc79245e678ae2..49b57a86a4f4c62af565372c0991249b94edc07b 100644 (file)
@@ -1,8 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#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 "oomd-manager-bus.h"
 #include "oomd-manager.h"
 #include "path-util.h"
 
@@ -396,6 +400,9 @@ void manager_free(Manager *m) {
         sd_event_source_unref(m->cgroup_context_event_source);
         sd_event_unref(m->event);
 
+        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        sd_bus_flush_close_unref(m->bus);
+
         hashmap_free(m->monitored_swap_cgroup_contexts);
         hashmap_free(m->monitored_mem_pressure_cgroup_contexts);
 
@@ -438,6 +445,35 @@ int manager_new(Manager **ret) {
         return 0;
 }
 
+static int manager_connect_bus(Manager *m) {
+        int r;
+
+        assert(m);
+        assert(!m->bus);
+
+        r = bus_open_system_watch_bind_with_description(&m->bus, "bus-api-oom");
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to bus: %m");
+
+        r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/oom1", "org.freedesktop.oom1.Manager", manager_vtable, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add manager object vtable: %m");
+
+        r = bus_log_control_api_register(m->bus);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.oom1", 0, NULL, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to request name: %m");
+
+        r = sd_bus_attach_event(m->bus, m->event, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach bus to event loop: %m");
+
+        return 0;
+}
+
 int manager_start(Manager *m, bool dry_run, int swap_used_limit, int mem_pressure_limit) {
         unsigned long l;
         int r;
@@ -454,6 +490,10 @@ int manager_start(Manager *m, bool dry_run, int swap_used_limit, int mem_pressur
         if (r < 0)
                 return r;
 
+        r = manager_connect_bus(m);
+        if (r < 0)
+                return r;
+
         r = acquire_managed_oom_connect(m);
         if (r < 0)
                 return r;
@@ -464,3 +504,46 @@ int manager_start(Manager *m, bool dry_run, int swap_used_limit, int mem_pressur
 
         return 0;
 }
+
+int manager_get_dump_string(Manager *m, char **ret) {
+        _cleanup_free_ char *dump = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        OomdCGroupContext *c;
+        size_t size;
+        char *key;
+        int r;
+
+        assert(m);
+        assert(ret);
+
+        f = open_memstream_unlocked(&dump, &size);
+        if (!f)
+                return -errno;
+
+        fprintf(f,
+                "Dry Run: %s\n"
+                "Swap Used Limit: %u%%\n"
+                "Default Memory Pressure Limit: %lu%%\n"
+                "System Context:\n",
+                yes_no(m->dry_run),
+                m->swap_used_limit,
+                LOAD_INT(m->default_mem_pressure_limit));
+        oomd_dump_system_context(&m->system_context, f, "\t");
+
+        fprintf(f, "Swap Monitored CGroups:\n");
+        HASHMAP_FOREACH_KEY(c, key, m->monitored_swap_cgroup_contexts)
+                oomd_dump_swap_cgroup_context(c, f, "\t");
+
+        fprintf(f, "Memory Pressure Monitored CGroups:\n");
+        HASHMAP_FOREACH_KEY(c, key, m->monitored_mem_pressure_cgroup_contexts)
+                oomd_dump_memory_pressure_cgroup_context(c, f, "\t");
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return r;
+
+        f = safe_fclose(f);
+
+        *ret = TAKE_PTR(dump);
+        return 0;
+}
index f546ecf4e75ad4e13eaadfafdf708c941d08b75f..b5c249799b457d923e2788aaca7baf1e0c14002c 100644 (file)
 typedef struct Manager Manager;
 
 struct Manager {
+        sd_bus *bus;
         sd_event *event;
 
+        Hashmap *polkit_registry;
+
         bool dry_run;
         unsigned swap_used_limit;
         loadavg_t default_mem_pressure_limit;
@@ -51,3 +54,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, int mem_pressure_limit);
+
+int manager_get_dump_string(Manager *m, char **ret);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_oomd_default);
index 4018c70b8e113055616f1d7f61e8474bcf0168fb..6cd4ba4f93138dada7df5d23f7a8c41d1c73fdbd 100644 (file)
@@ -385,3 +385,67 @@ int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path)
 
         return 0;
 }
+
+void oomd_dump_swap_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix) {
+        char swap[FORMAT_BYTES_MAX];
+
+        assert(ctx);
+        assert(f);
+
+        if (!empty_or_root(ctx->path))
+                fprintf(f,
+                        "%sPath: %s\n"
+                        "%s\tSwap Usage: %s\n",
+                        strempty(prefix), ctx->path,
+                        strempty(prefix), format_bytes(swap, sizeof(swap), ctx->swap_usage));
+        else
+                fprintf(f,
+                        "%sPath: %s\n"
+                        "%s\tSwap Usage: (see System Context)\n",
+                        strempty(prefix), ctx->path,
+                        strempty(prefix));
+}
+
+void oomd_dump_memory_pressure_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix) {
+        char tbuf[FORMAT_TIMESPAN_MAX], mem_use[FORMAT_BYTES_MAX];
+        char mem_min[FORMAT_BYTES_MAX], mem_low[FORMAT_BYTES_MAX];
+
+        assert(ctx);
+        assert(f);
+
+        fprintf(f,
+                "%sPath: %s\n"
+                "%s\tMemory Pressure Limit: %lu%%\n"
+                "%s\tPressure: Avg10: %lu.%02lu Avg60: %lu.%02lu Avg300: %lu.%02lu Total: %s\n"
+                "%s\tCurrent Memory Usage: %s\n",
+                strempty(prefix), ctx->path,
+                strempty(prefix), LOAD_INT(ctx->mem_pressure_limit),
+                strempty(prefix),
+                LOAD_INT(ctx->memory_pressure.avg10), LOAD_FRAC(ctx->memory_pressure.avg10),
+                LOAD_INT(ctx->memory_pressure.avg60), LOAD_FRAC(ctx->memory_pressure.avg60),
+                LOAD_INT(ctx->memory_pressure.avg300), LOAD_FRAC(ctx->memory_pressure.avg300),
+                format_timespan(tbuf, sizeof(tbuf), ctx->memory_pressure.total, USEC_PER_SEC),
+                strempty(prefix), format_bytes(mem_use, sizeof(mem_use), ctx->current_memory_usage));
+
+        if (!empty_or_root(ctx->path))
+                fprintf(f,
+                        "%s\tMemory Min: %s\n"
+                        "%s\tMemory Low: %s\n"
+                        "%s\tPgscan: %" PRIu64 "\n",
+                        strempty(prefix), format_bytes_cgroup_protection(mem_min, sizeof(mem_min), ctx->memory_min),
+                        strempty(prefix), format_bytes_cgroup_protection(mem_low, sizeof(mem_low), ctx->memory_low),
+                        strempty(prefix), ctx->pgscan);
+}
+
+void oomd_dump_system_context(const OomdSystemContext *ctx, FILE *f, const char *prefix) {
+        char used[FORMAT_BYTES_MAX], total[FORMAT_BYTES_MAX];
+
+        assert(ctx);
+        assert(f);
+
+        fprintf(f,
+                "%sSwap: Used: %s Total: %s\n",
+                strempty(prefix),
+                format_bytes(used, sizeof(used), ctx->swap_used),
+                format_bytes(total, sizeof(total), ctx->swap_total));
+}
index 6d34d91cc2bdbefc4be3fcbf472adad986637e25..cfd717a0181bc9676043ae34b9175d5c5f82ff2f 100644 (file)
@@ -106,3 +106,7 @@ int oomd_system_context_acquire(const char *proc_swaps_path, OomdSystemContext *
  * `old_h` is used to get data used to calculate prior interval information. `old_h` can be NULL in which case there
  * was no prior data to reference. */
 int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path);
+
+void oomd_dump_swap_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
+void oomd_dump_memory_pressure_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
+void oomd_dump_system_context(const OomdSystemContext *ctx, FILE *f, const char *prefix);
diff --git a/src/oom/org.freedesktop.oom1.conf b/src/oom/org.freedesktop.oom1.conf
new file mode 100644 (file)
index 0000000..48b526f
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<busconfig>
+
+        <policy user="systemd-oom">
+                <allow own="org.freedesktop.oom1"/>
+                <allow send_destination="org.freedesktop.oom1"/>
+                <allow receive_sender="org.freedesktop.oom1"/>
+        </policy>
+
+        <policy user="root">
+                <allow send_destination="org.freedesktop.oom1"/>
+        </policy>
+
+        <policy context="default">
+                <deny send_destination="org.freedesktop.oom1"/>
+
+                <!-- Generic interfaces -->
+
+                <allow send_destination="org.freedesktop.oom1"
+                       send_interface="org.freedesktop.DBus.Introspectable"/>
+
+                <allow send_destination="org.freedesktop.oom1"
+                       send_interface="org.freedesktop.DBus.Peer"/>
+
+                <allow send_destination="org.freedesktop.oom1"
+                       send_interface="org.freedesktop.DBus.Properties"
+                       send_member="Get"/>
+
+                <allow send_destination="org.freedesktop.oom1"
+                       send_interface="org.freedesktop.DBus.Properties"
+                       send_member="GetAll"/>
+
+                <!-- Manager interface -->
+
+                <allow send_destination="org.freedesktop.oom1"
+                       send_interface="org.freedesktop.oom1.Manager"
+                       send_member="DumpByFileDescriptor"/>
+
+                <allow receive_sender="org.freedesktop.oom1"/>
+        </policy>
+
+</busconfig>