systemd-oomd: dbus hook ups and oomctl CLI
This commit is contained in:
parent
fe8d22fb09
commit
5c616ecfcd
10
meson.build
10
meson.build
|
@ -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
|
||||
|
|
|
@ -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
138
src/oom/oomctl.c
Normal 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);
|
36
src/oom/oomd-manager-bus.c
Normal file
36
src/oom/oomd-manager-bus.c
Normal 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
|
||||
};
|
8
src/oom/oomd-manager-bus.h
Normal file
8
src/oom/oomd-manager-bus.h
Normal 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[];
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
47
src/oom/org.freedesktop.oom1.conf
Normal file
47
src/oom/org.freedesktop.oom1.conf
Normal 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>
|
Loading…
Reference in a new issue