]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/oom/oomd-util.c
selinux: support infering SELinux label also from socket not connected to stdin
[thirdparty/systemd.git] / src / oom / oomd-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
61ff7397
AZ
2
3#include <sys/xattr.h>
4#include <unistd.h>
5
6#include "fd-util.h"
47136b9d 7#include "fileio.h"
61ff7397
AZ
8#include "format-util.h"
9#include "oomd-util.h"
10#include "parse-util.h"
11#include "path-util.h"
12#include "procfs-util.h"
13#include "signal-util.h"
14#include "sort-util.h"
15#include "stat-util.h"
16#include "stdio-util.h"
17
18DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
19 oomd_cgroup_ctx_hash_ops,
20 char,
21 string_hash_func,
22 string_compare_func,
23 OomdCGroupContext,
24 oomd_cgroup_context_free);
25
26static int log_kill(pid_t pid, int sig, void *userdata) {
27 log_debug("oomd attempting to kill " PID_FMT " with %s", pid, signal_to_string(sig));
28 return 0;
29}
30
31static int increment_oomd_xattr(const char *path, const char *xattr, uint64_t num_procs_killed) {
32 _cleanup_free_ char *value = NULL;
33 char buf[DECIMAL_STR_MAX(uint64_t) + 1];
34 uint64_t curr_count = 0;
35 int r;
36
37 assert(path);
38 assert(xattr);
39
40 r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, path, xattr, &value);
41 if (r < 0 && r != -ENODATA)
42 return r;
43
44 if (!isempty(value)) {
45 r = safe_atou64(value, &curr_count);
46 if (r < 0)
47 return r;
48 }
49
50 if (curr_count > UINT64_MAX - num_procs_killed)
51 return -EOVERFLOW;
52
53 xsprintf(buf, "%"PRIu64, curr_count + num_procs_killed);
54 r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, path, xattr, buf, strlen(buf), 0);
55 if (r < 0)
56 return r;
57
58 return 0;
59}
60
61OomdCGroupContext *oomd_cgroup_context_free(OomdCGroupContext *ctx) {
62 if (!ctx)
63 return NULL;
64
65 free(ctx->path);
66 return mfree(ctx);
67}
68
69int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret) {
70 _cleanup_set_free_ Set *targets = NULL;
71 OomdCGroupContext *ctx;
72 char *key;
73 int r;
74
75 assert(h);
76 assert(ret);
77
78 targets = set_new(NULL);
79 if (!targets)
80 return -ENOMEM;
81
82 HASHMAP_FOREACH_KEY(ctx, key, h) {
83 if (ctx->memory_pressure.avg10 > ctx->mem_pressure_limit) {
84 usec_t diff;
85
69c8f025
AZ
86 if (ctx->mem_pressure_limit_hit_start == 0)
87 ctx->mem_pressure_limit_hit_start = now(CLOCK_MONOTONIC);
61ff7397 88
69c8f025 89 diff = now(CLOCK_MONOTONIC) - ctx->mem_pressure_limit_hit_start;
61ff7397
AZ
90 if (diff >= duration) {
91 r = set_put(targets, ctx);
92 if (r < 0)
93 return -ENOMEM;
94 }
95 } else
69c8f025 96 ctx->mem_pressure_limit_hit_start = 0;
61ff7397
AZ
97 }
98
99 if (!set_isempty(targets)) {
100 *ret = TAKE_PTR(targets);
101 return 1;
102 }
103
104 *ret = NULL;
105 return 0;
106}
107
37d8020c
AZ
108uint64_t oomd_pgscan_rate(const OomdCGroupContext *c) {
109 uint64_t last_pgscan;
110
111 assert(c);
112
113 /* If last_pgscan > pgscan, assume the cgroup was recreated and reset last_pgscan to zero.
114 * pgscan is monotonic and in practice should not decrease (except in the recreation case). */
115 last_pgscan = c->last_pgscan;
116 if (c->last_pgscan > c->pgscan) {
117 log_debug("Last pgscan %"PRIu64" greater than current pgscan %"PRIu64" for %s. Using last pgscan of zero.",
118 c->last_pgscan, c->pgscan, c->path);
119 last_pgscan = 0;
120 }
121
122 return c->pgscan - last_pgscan;
123}
124
eeeaa422
AZ
125bool oomd_mem_free_below(const OomdSystemContext *ctx, int threshold_permyriad) {
126 uint64_t mem_threshold;
127
128 assert(ctx);
129 assert(threshold_permyriad <= 10000);
130
131 mem_threshold = ctx->mem_total * threshold_permyriad / (uint64_t) 10000;
132 return (ctx->mem_total - ctx->mem_used) < mem_threshold;
133}
134
d06e7fb5 135bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad) {
61ff7397
AZ
136 uint64_t swap_threshold;
137
138 assert(ctx);
d06e7fb5 139 assert(threshold_permyriad <= 10000);
61ff7397 140
d06e7fb5 141 swap_threshold = ctx->swap_total * threshold_permyriad / (uint64_t) 10000;
61ff7397
AZ
142 return (ctx->swap_total - ctx->swap_used) < swap_threshold;
143}
144
145int oomd_sort_cgroup_contexts(Hashmap *h, oomd_compare_t compare_func, const char *prefix, OomdCGroupContext ***ret) {
146 _cleanup_free_ OomdCGroupContext **sorted = NULL;
147 OomdCGroupContext *item;
148 size_t k = 0;
149
150 assert(h);
151 assert(compare_func);
152 assert(ret);
153
154 sorted = new0(OomdCGroupContext*, hashmap_size(h));
155 if (!sorted)
156 return -ENOMEM;
157
158 HASHMAP_FOREACH(item, h) {
59331b8e
AZ
159 /* Skip over cgroups that are not valid candidates or are explicitly marked for omission */
160 if ((item->path && prefix && !path_startswith(item->path, prefix)) || item->preference == MANAGED_OOM_PREFERENCE_OMIT)
61ff7397
AZ
161 continue;
162
163 sorted[k++] = item;
164 }
165
166 typesafe_qsort(sorted, k, compare_func);
167
168 *ret = TAKE_PTR(sorted);
169
170 assert(k <= INT_MAX);
171 return (int) k;
172}
173
174int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
175 _cleanup_set_free_ Set *pids_killed = NULL;
176 int r;
177
178 assert(path);
179
180 if (dry_run) {
181 _cleanup_free_ char *cg_path = NULL;
182
183 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &cg_path);
184 if (r < 0)
185 return r;
186
187 log_debug("oomd dry-run: Would have tried to kill %s with recurse=%s", cg_path, true_false(recurse));
188 return 0;
189 }
190
191 pids_killed = set_new(NULL);
192 if (!pids_killed)
193 return -ENOMEM;
194
195 if (recurse)
196 r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
197 else
198 r = cg_kill(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
199 if (r < 0)
200 return r;
201
e3038333 202 r = increment_oomd_xattr(path, "user.oomd_kill", set_size(pids_killed));
61ff7397 203 if (r < 0)
e3038333 204 log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
61ff7397
AZ
205
206 return set_size(pids_killed) != 0;
207}
208
37a7e159 209int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected) {
61ff7397 210 _cleanup_free_ OomdCGroupContext **sorted = NULL;
f94a80ab 211 int n, r, ret = 0;
61ff7397
AZ
212
213 assert(h);
37a7e159 214 assert(ret_selected);
61ff7397 215
f94a80ab
ZJS
216 n = oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, prefix, &sorted);
217 if (n < 0)
218 return n;
61ff7397 219
f94a80ab 220 for (int i = 0; i < n; i++) {
37a7e159
AZ
221 /* Skip cgroups with no reclaim and memory usage; it won't alleviate pressure.
222 * Continue since there might be "avoid" cgroups at the end. */
74f834e9 223 if (sorted[i]->pgscan == 0 && sorted[i]->current_memory_usage == 0)
59331b8e 224 continue;
61ff7397
AZ
225
226 r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
37a7e159
AZ
227 if (r == 0)
228 continue; /* We didn't find anything to kill */
229 if (r == -ENOMEM)
230 return r; /* Treat oom as a hard error */
231 if (r < 0) {
232 if (ret == 0)
233 ret = r;
234 continue; /* Try to find something else to kill */
235 }
236
237 char *selected = strdup(sorted[i]->path);
238 if (!selected)
239 return -ENOMEM;
240 *ret_selected = selected;
241 return 1;
61ff7397
AZ
242 }
243
37a7e159 244 return ret;
61ff7397
AZ
245}
246
685b0985 247int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected) {
61ff7397 248 _cleanup_free_ OomdCGroupContext **sorted = NULL;
f94a80ab 249 int n, r, ret = 0;
61ff7397
AZ
250
251 assert(h);
37a7e159 252 assert(ret_selected);
61ff7397 253
f94a80ab
ZJS
254 n = oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted);
255 if (n < 0)
256 return n;
61ff7397 257
685b0985
AZ
258 /* Try to kill cgroups with non-zero swap usage until we either succeed in killing or we get to a cgroup with
259 * no swap usage. Threshold killing only cgroups with more than threshold swap usage. */
f94a80ab 260 for (int i = 0; i < n; i++) {
685b0985
AZ
261 /* Skip over cgroups with not enough swap usage. Don't break since there might be "avoid"
262 * cgroups at the end. */
263 if (sorted[i]->swap_usage <= threshold_usage)
59331b8e 264 continue;
61ff7397
AZ
265
266 r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
37a7e159
AZ
267 if (r == 0)
268 continue; /* We didn't find anything to kill */
269 if (r == -ENOMEM)
270 return r; /* Treat oom as a hard error */
271 if (r < 0) {
272 if (ret == 0)
273 ret = r;
274 continue; /* Try to find something else to kill */
275 }
276
277 char *selected = strdup(sorted[i]->path);
278 if (!selected)
279 return -ENOMEM;
280 *ret_selected = selected;
281 return 1;
61ff7397
AZ
282 }
283
37a7e159 284 return ret;
61ff7397
AZ
285}
286
287int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) {
288 _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
289 _cleanup_free_ char *p = NULL, *val = NULL;
290 bool is_root;
59331b8e 291 uid_t uid;
61ff7397
AZ
292 int r;
293
294 assert(path);
295 assert(ret);
296
297 ctx = new0(OomdCGroupContext, 1);
298 if (!ctx)
299 return -ENOMEM;
300
301 is_root = empty_or_root(path);
59331b8e 302 ctx->preference = MANAGED_OOM_PREFERENCE_NONE;
61ff7397
AZ
303
304 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, "memory.pressure", &p);
305 if (r < 0)
306 return log_debug_errno(r, "Error getting cgroup memory pressure path from %s: %m", path);
307
308 r = read_resource_pressure(p, PRESSURE_TYPE_FULL, &ctx->memory_pressure);
309 if (r < 0)
310 return log_debug_errno(r, "Error parsing memory pressure from %s: %m", p);
311
59331b8e
AZ
312 r = cg_get_owner(SYSTEMD_CGROUP_CONTROLLER, path, &uid);
313 if (r < 0)
314 log_debug_errno(r, "Failed to get owner/group from %s: %m", path);
315 else if (uid == 0) {
316 /* Ignore most errors when reading the xattr since it is usually unset and cgroup xattrs are only used
317 * as an optional feature of systemd-oomd (and the system might not even support them). */
318 r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, path, "user.oomd_avoid");
319 if (r == -ENOMEM)
320 return r;
321 ctx->preference = r == 1 ? MANAGED_OOM_PREFERENCE_AVOID : ctx->preference;
322
323 r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, path, "user.oomd_omit");
324 if (r == -ENOMEM)
325 return r;
326 ctx->preference = r == 1 ? MANAGED_OOM_PREFERENCE_OMIT : ctx->preference;
327 }
328
61ff7397
AZ
329 if (is_root) {
330 r = procfs_memory_get_used(&ctx->current_memory_usage);
331 if (r < 0)
332 return log_debug_errno(r, "Error getting memory used from procfs: %m");
333 } else {
334 r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.current", &ctx->current_memory_usage);
335 if (r < 0)
336 return log_debug_errno(r, "Error getting memory.current from %s: %m", path);
337
338 r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.min", &ctx->memory_min);
339 if (r < 0)
340 return log_debug_errno(r, "Error getting memory.min from %s: %m", path);
341
342 r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.low", &ctx->memory_low);
343 if (r < 0)
344 return log_debug_errno(r, "Error getting memory.low from %s: %m", path);
345
346 r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.swap.current", &ctx->swap_usage);
13540027
DS
347 if (r == -ENODATA)
348 /* The kernel can be compiled without support for memory.swap.* files,
349 * or it can be disabled with boot param 'swapaccount=0' */
350 log_once(LOG_WARNING, "No kernel support for memory.swap.current from %s (try boot param swapaccount=1), ignoring.", path);
351 else if (r < 0)
61ff7397
AZ
352 return log_debug_errno(r, "Error getting memory.swap.current from %s: %m", path);
353
354 r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, path, "memory.stat", STRV_MAKE("pgscan"), &val);
355 if (r < 0)
356 return log_debug_errno(r, "Error getting pgscan from memory.stat under %s: %m", path);
357
358 r = safe_atou64(val, &ctx->pgscan);
359 if (r < 0)
360 return log_debug_errno(r, "Error converting pgscan value to uint64_t: %m");
361 }
362
363 ctx->path = strdup(empty_to_root(path));
364 if (!ctx->path)
365 return -ENOMEM;
366
367 *ret = TAKE_PTR(ctx);
368 return 0;
369}
370
47136b9d 371int oomd_system_context_acquire(const char *proc_meminfo_path, OomdSystemContext *ret) {
61ff7397 372 _cleanup_fclose_ FILE *f = NULL;
47136b9d 373 unsigned field_filled = 0;
61ff7397 374 OomdSystemContext ctx = {};
eeeaa422 375 uint64_t mem_free, swap_free;
61ff7397
AZ
376 int r;
377
47136b9d 378 assert(proc_meminfo_path);
61ff7397
AZ
379 assert(ret);
380
47136b9d 381 f = fopen(proc_meminfo_path, "re");
61ff7397
AZ
382 if (!f)
383 return -errno;
384
61ff7397 385 for (;;) {
47136b9d
AZ
386 _cleanup_free_ char *line = NULL;
387 char *word;
61ff7397 388
47136b9d
AZ
389 r = read_line(f, LONG_LINE_MAX, &line);
390 if (r < 0)
391 return r;
392 if (r == 0)
393 return -EINVAL;
394
eeeaa422 395 if ((word = startswith(line, "MemTotal:"))) {
47136b9d 396 field_filled |= 1U << 0;
eeeaa422
AZ
397 r = convert_meminfo_value_to_uint64_bytes(word, &ctx.mem_total);
398 } else if ((word = startswith(line, "MemFree:"))) {
399 field_filled |= 1U << 1;
400 r = convert_meminfo_value_to_uint64_bytes(word, &mem_free);
401 } else if ((word = startswith(line, "SwapTotal:"))) {
402 field_filled |= 1U << 2;
47136b9d
AZ
403 r = convert_meminfo_value_to_uint64_bytes(word, &ctx.swap_total);
404 } else if ((word = startswith(line, "SwapFree:"))) {
eeeaa422 405 field_filled |= 1U << 3;
47136b9d
AZ
406 r = convert_meminfo_value_to_uint64_bytes(word, &swap_free);
407 } else
408 continue;
61ff7397 409
47136b9d
AZ
410 if (r < 0)
411 return log_debug_errno(r, "Error converting '%s' from %s to uint64_t: %m", line, proc_meminfo_path);
61ff7397 412
eeeaa422 413 if (field_filled == 15U)
47136b9d
AZ
414 break;
415 }
61ff7397 416
eeeaa422 417 if (field_filled != 15U)
47136b9d 418 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "%s is missing expected fields", proc_meminfo_path);
61ff7397 419
eeeaa422
AZ
420 if (mem_free > ctx.mem_total)
421 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
422 "MemFree (%" PRIu64 ") cannot be greater than MemTotal (%" PRIu64 ") %m",
423 mem_free,
424 ctx.mem_total);
425
47136b9d
AZ
426 if (swap_free > ctx.swap_total)
427 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
428 "SwapFree (%" PRIu64 ") cannot be greater than SwapTotal (%" PRIu64 ") %m",
429 swap_free,
430 ctx.swap_total);
431
eeeaa422 432 ctx.mem_used = ctx.mem_total - mem_free;
47136b9d 433 ctx.swap_used = ctx.swap_total - swap_free;
61ff7397
AZ
434
435 *ret = ctx;
436 return 0;
437}
438
439int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path) {
440 _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *curr_ctx = NULL;
45da27fa 441 OomdCGroupContext *old_ctx;
61ff7397
AZ
442 int r;
443
444 assert(new_h);
445 assert(path);
446
50c0578b
AZ
447 path = empty_to_root(path);
448
61ff7397
AZ
449 r = oomd_cgroup_context_acquire(path, &curr_ctx);
450 if (r < 0)
451 return log_debug_errno(r, "Failed to get OomdCGroupContext for %s: %m", path);
452
50c0578b
AZ
453 assert_se(streq(path, curr_ctx->path));
454
61ff7397
AZ
455 old_ctx = hashmap_get(old_h, path);
456 if (old_ctx) {
457 curr_ctx->last_pgscan = old_ctx->pgscan;
458 curr_ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
69c8f025 459 curr_ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
df637ede 460 curr_ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
61ff7397
AZ
461 }
462
df637ede
AZ
463 if (oomd_pgscan_rate(curr_ctx) > 0)
464 curr_ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
465
45da27fa 466 r = hashmap_put(new_h, curr_ctx->path, curr_ctx);
61ff7397
AZ
467 if (r < 0)
468 return r;
469
45da27fa 470 TAKE_PTR(curr_ctx);
61ff7397
AZ
471 return 0;
472}
5c616ecf 473
b037a6da
AZ
474void oomd_update_cgroup_contexts_between_hashmaps(Hashmap *old_h, Hashmap *curr_h) {
475 OomdCGroupContext *ctx;
476
477 assert(old_h);
478 assert(curr_h);
479
480 HASHMAP_FOREACH(ctx, curr_h) {
481 OomdCGroupContext *old_ctx;
482
483 old_ctx = hashmap_get(old_h, ctx->path);
484 if (!old_ctx)
485 continue;
486
487 ctx->last_pgscan = old_ctx->pgscan;
488 ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
69c8f025 489 ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
df637ede
AZ
490 ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
491
492 if (oomd_pgscan_rate(ctx) > 0)
493 ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
b037a6da
AZ
494 }
495}
496
5c616ecf
AZ
497void oomd_dump_swap_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix) {
498 char swap[FORMAT_BYTES_MAX];
499
500 assert(ctx);
501 assert(f);
502
503 if (!empty_or_root(ctx->path))
504 fprintf(f,
505 "%sPath: %s\n"
506 "%s\tSwap Usage: %s\n",
507 strempty(prefix), ctx->path,
508 strempty(prefix), format_bytes(swap, sizeof(swap), ctx->swap_usage));
509 else
510 fprintf(f,
511 "%sPath: %s\n"
512 "%s\tSwap Usage: (see System Context)\n",
513 strempty(prefix), ctx->path,
514 strempty(prefix));
515}
516
517void oomd_dump_memory_pressure_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix) {
518 char tbuf[FORMAT_TIMESPAN_MAX], mem_use[FORMAT_BYTES_MAX];
519 char mem_min[FORMAT_BYTES_MAX], mem_low[FORMAT_BYTES_MAX];
520
521 assert(ctx);
522 assert(f);
523
524 fprintf(f,
525 "%sPath: %s\n"
0a9f9344 526 "%s\tMemory Pressure Limit: %lu.%02lu%%\n"
5c616ecf
AZ
527 "%s\tPressure: Avg10: %lu.%02lu Avg60: %lu.%02lu Avg300: %lu.%02lu Total: %s\n"
528 "%s\tCurrent Memory Usage: %s\n",
529 strempty(prefix), ctx->path,
0a9f9344 530 strempty(prefix), LOAD_INT(ctx->mem_pressure_limit), LOAD_FRAC(ctx->mem_pressure_limit),
5c616ecf
AZ
531 strempty(prefix),
532 LOAD_INT(ctx->memory_pressure.avg10), LOAD_FRAC(ctx->memory_pressure.avg10),
533 LOAD_INT(ctx->memory_pressure.avg60), LOAD_FRAC(ctx->memory_pressure.avg60),
534 LOAD_INT(ctx->memory_pressure.avg300), LOAD_FRAC(ctx->memory_pressure.avg300),
535 format_timespan(tbuf, sizeof(tbuf), ctx->memory_pressure.total, USEC_PER_SEC),
536 strempty(prefix), format_bytes(mem_use, sizeof(mem_use), ctx->current_memory_usage));
537
538 if (!empty_or_root(ctx->path))
539 fprintf(f,
540 "%s\tMemory Min: %s\n"
541 "%s\tMemory Low: %s\n"
bb081240
AZ
542 "%s\tPgscan: %" PRIu64 "\n"
543 "%s\tLast Pgscan: %" PRIu64 "\n",
5c616ecf
AZ
544 strempty(prefix), format_bytes_cgroup_protection(mem_min, sizeof(mem_min), ctx->memory_min),
545 strempty(prefix), format_bytes_cgroup_protection(mem_low, sizeof(mem_low), ctx->memory_low),
bb081240
AZ
546 strempty(prefix), ctx->pgscan,
547 strempty(prefix), ctx->last_pgscan);
5c616ecf
AZ
548}
549
550void oomd_dump_system_context(const OomdSystemContext *ctx, FILE *f, const char *prefix) {
eeeaa422
AZ
551 char mem_used[FORMAT_BYTES_MAX], mem_total[FORMAT_BYTES_MAX];
552 char swap_used[FORMAT_BYTES_MAX], swap_total[FORMAT_BYTES_MAX];
5c616ecf
AZ
553
554 assert(ctx);
555 assert(f);
556
557 fprintf(f,
eeeaa422 558 "%sMemory: Used: %s Total: %s\n"
5c616ecf
AZ
559 "%sSwap: Used: %s Total: %s\n",
560 strempty(prefix),
eeeaa422
AZ
561 format_bytes(mem_used, sizeof(mem_used), ctx->mem_used),
562 format_bytes(mem_total, sizeof(mem_total), ctx->mem_total),
563 strempty(prefix),
564 format_bytes(swap_used, sizeof(swap_used), ctx->swap_used),
565 format_bytes(swap_total, sizeof(swap_total), ctx->swap_total));
5c616ecf 566}