systemd-oomd: dbus hook ups and oomctl CLI

This commit is contained in:
Anita Zhang 2020-09-08 23:46:27 -07:00
parent fe8d22fb09
commit 5c616ecfcd
10 changed files with 406 additions and 0 deletions

View 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

View 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

138
src/oom/oomctl.c Normal file
View File

@ -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);

View File

@ -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
};

View File

@ -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[];

View 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;
}

View File

@ -25,8 +25,11 @@
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);

View 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));
}

View 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);

View File

@ -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>