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