]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/oom/oomd.c
Merge pull request #24686 from d4nuu8/delta_output
[thirdparty/systemd.git] / src / oom / oomd.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4
5 #include "bus-log-control-api.h"
6 #include "bus-object.h"
7 #include "cgroup-util.h"
8 #include "conf-parser.h"
9 #include "daemon-util.h"
10 #include "fileio.h"
11 #include "log.h"
12 #include "main-func.h"
13 #include "oomd-manager-bus.h"
14 #include "oomd-manager.h"
15 #include "parse-util.h"
16 #include "pretty-print.h"
17 #include "psi-util.h"
18 #include "signal-util.h"
19
20 static bool arg_dry_run = false;
21 static int arg_swap_used_limit_permyriad = -1;
22 static int arg_mem_pressure_limit_permyriad = -1;
23 static usec_t arg_mem_pressure_usec = 0;
24
25 static int parse_config(void) {
26 static const ConfigTableItem items[] = {
27 { "OOM", "SwapUsedLimit", config_parse_permyriad, 0, &arg_swap_used_limit_permyriad },
28 { "OOM", "DefaultMemoryPressureLimit", config_parse_permyriad, 0, &arg_mem_pressure_limit_permyriad },
29 { "OOM", "DefaultMemoryPressureDurationSec", config_parse_sec, 0, &arg_mem_pressure_usec },
30 {}
31 };
32
33 return config_parse_many_nulstr(PKGSYSCONFDIR "/oomd.conf",
34 CONF_PATHS_NULSTR("systemd/oomd.conf.d"),
35 "OOM\0",
36 config_item_table_lookup,
37 items,
38 CONFIG_PARSE_WARN,
39 NULL,
40 NULL);
41 }
42
43 static int help(void) {
44 _cleanup_free_ char *link = NULL;
45 int r;
46
47 r = terminal_urlify_man("systemd-oomd", "1", &link);
48 if (r < 0)
49 return log_oom();
50
51 printf("%s [OPTIONS...]\n\n"
52 "Run the userspace out-of-memory (OOM) killer.\n\n"
53 " -h --help Show this help\n"
54 " --version Show package version\n"
55 " --dry-run Only print destructive actions instead of doing them\n"
56 " --bus-introspect=PATH Write D-Bus XML introspection data\n"
57 "\nSee the %s for details.\n",
58 program_invocation_short_name,
59 link);
60
61 return 0;
62 }
63
64 static int parse_argv(int argc, char *argv[]) {
65 enum {
66 ARG_VERSION = 0x100,
67 ARG_DRY_RUN,
68 ARG_BUS_INTROSPECT,
69 };
70
71 static const struct option options[] = {
72 { "help", no_argument, NULL, 'h' },
73 { "version", no_argument, NULL, ARG_VERSION },
74 { "dry-run", no_argument, NULL, ARG_DRY_RUN },
75 { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT },
76 {}
77 };
78
79 int c;
80
81 assert(argc >= 0);
82 assert(argv);
83
84 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
85
86 switch (c) {
87
88 case 'h':
89 return help();
90
91 case ARG_VERSION:
92 return version();
93
94 case ARG_DRY_RUN:
95 arg_dry_run = true;
96 break;
97
98 case ARG_BUS_INTROSPECT:
99 return bus_introspect_implementations(
100 stdout,
101 optarg,
102 BUS_IMPLEMENTATIONS(&manager_object,
103 &log_control_object));
104
105 case '?':
106 return -EINVAL;
107
108 default:
109 assert_not_reached();
110 }
111
112 if (optind < argc)
113 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
114 "This program takes no arguments.");
115
116 return 1;
117 }
118
119 static int run(int argc, char *argv[]) {
120 _unused_ _cleanup_(notify_on_cleanup) const char *notify_msg = NULL;
121 _cleanup_(manager_freep) Manager *m = NULL;
122 _cleanup_free_ char *swap = NULL;
123 unsigned long long s = 0;
124 CGroupMask mask;
125 int r;
126
127 log_setup();
128
129 r = parse_argv(argc, argv);
130 if (r <= 0)
131 return r;
132
133 r = parse_config();
134 if (r < 0)
135 return r;
136
137 /* Do some basic requirement checks for running systemd-oomd. It's not exhaustive as some of the other
138 * requirements do not have a reliable means to check for in code. */
139
140 int n = sd_listen_fds(0);
141 if (n > 1)
142 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Received too many file descriptors");
143
144 int fd = n == 1 ? SD_LISTEN_FDS_START : -1;
145
146 /* SwapTotal is always available in /proc/meminfo and defaults to 0, even on swap-disabled kernels. */
147 r = get_proc_field("/proc/meminfo", "SwapTotal", WHITESPACE, &swap);
148 if (r < 0)
149 return log_error_errno(r, "Failed to get SwapTotal from /proc/meminfo: %m");
150
151 r = safe_atollu(swap, &s);
152 if (r < 0 || s == 0)
153 log_warning("Swap is currently not detected; memory pressure usage will be degraded");
154
155 if (!is_pressure_supported())
156 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Pressure Stall Information (PSI) is not supported");
157
158 r = cg_all_unified();
159 if (r < 0)
160 return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
161 if (r == 0)
162 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Requires the unified cgroups hierarchy");
163
164 r = cg_mask_supported(&mask);
165 if (r < 0)
166 return log_error_errno(r, "Failed to get supported cgroup controllers: %m");
167
168 if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
169 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Requires the cgroup memory controller.");
170
171 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
172
173 if (arg_mem_pressure_usec > 0 && arg_mem_pressure_usec < 1 * USEC_PER_SEC)
174 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DefaultMemoryPressureDurationSec= must be 0 or at least 1s");
175
176 r = manager_new(&m);
177 if (r < 0)
178 return log_error_errno(r, "Failed to create manager: %m");
179
180 r = manager_start(
181 m,
182 arg_dry_run,
183 arg_swap_used_limit_permyriad,
184 arg_mem_pressure_limit_permyriad,
185 arg_mem_pressure_usec,
186 fd);
187 if (r < 0)
188 return log_error_errno(r, "Failed to start up daemon: %m");
189
190 notify_msg = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
191
192 log_debug("systemd-oomd started%s.", arg_dry_run ? " in dry run mode" : "");
193
194 r = sd_event_loop(m->event);
195 if (r < 0)
196 return log_error_errno(r, "Event loop failed: %m");
197
198 return 0;
199 }
200
201 DEFINE_MAIN_FUNCTION(run);