]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
19d22d43 LP |
2 | |
3 | #include "core-varlink.h" | |
35cd0ba5 | 4 | #include "mkdir-label.h" |
e30bbc90 | 5 | #include "strv.h" |
19d22d43 LP |
6 | #include "user-util.h" |
7 | #include "varlink.h" | |
abef4a7b LP |
8 | #include "varlink-io.systemd.UserDatabase.h" |
9 | #include "varlink-io.systemd.ManagedOOM.h" | |
19d22d43 LP |
10 | |
11 | typedef struct LookupParameters { | |
12 | const char *user_name; | |
13 | const char *group_name; | |
14 | union { | |
15 | uid_t uid; | |
16 | gid_t gid; | |
17 | }; | |
18 | const char *service; | |
19 | } LookupParameters; | |
20 | ||
e30bbc90 AZ |
21 | static const char* const managed_oom_mode_properties[] = { |
22 | "ManagedOOMSwap", | |
23 | "ManagedOOMMemoryPressure", | |
24 | }; | |
25 | ||
19d22d43 LP |
26 | static int build_user_json(const char *user_name, uid_t uid, JsonVariant **ret) { |
27 | assert(user_name); | |
28 | assert(uid_is_valid(uid)); | |
29 | assert(ret); | |
30 | ||
31 | return json_build(ret, JSON_BUILD_OBJECT( | |
4ca739e2 ZJS |
32 | JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT( |
33 | JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)), | |
34 | JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)), | |
35 | JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(uid)), | |
0cdf6b14 LP |
36 | JSON_BUILD_PAIR("realName", JSON_BUILD_CONST_STRING("Dynamic User")), |
37 | JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/")), | |
38 | JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)), | |
4ca739e2 | 39 | JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)), |
0cdf6b14 LP |
40 | JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.DynamicUser")), |
41 | JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("dynamic")))))); | |
19d22d43 LP |
42 | } |
43 | ||
44 | static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) { | |
45 | assert(p); | |
46 | ||
47 | if (p->user_name && !streq(name, p->user_name)) | |
48 | return false; | |
49 | ||
50 | if (uid_is_valid(p->uid) && uid != p->uid) | |
51 | return false; | |
52 | ||
53 | return true; | |
54 | } | |
55 | ||
e30bbc90 | 56 | static int build_managed_oom_json_array_element(Unit *u, const char *property, JsonVariant **ret_v) { |
2801d36e | 57 | bool use_limit = false; |
e30bbc90 AZ |
58 | CGroupContext *c; |
59 | const char *mode; | |
e30bbc90 AZ |
60 | |
61 | assert(u); | |
62 | assert(property); | |
63 | assert(ret_v); | |
64 | ||
65 | if (!UNIT_VTABLE(u)->can_set_managed_oom) | |
66 | return -EOPNOTSUPP; | |
67 | ||
68 | c = unit_get_cgroup_context(u); | |
69 | if (!c) | |
70 | return -EINVAL; | |
71 | ||
72 | if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u))) | |
73 | /* systemd-oomd should always treat inactive units as though they didn't enable any action since they | |
74 | * should not have a valid cgroup */ | |
75 | mode = managed_oom_mode_to_string(MANAGED_OOM_AUTO); | |
76 | else if (streq(property, "ManagedOOMSwap")) | |
77 | mode = managed_oom_mode_to_string(c->moom_swap); | |
2801d36e | 78 | else if (streq(property, "ManagedOOMMemoryPressure")) { |
e30bbc90 | 79 | mode = managed_oom_mode_to_string(c->moom_mem_pressure); |
2801d36e AZ |
80 | use_limit = true; |
81 | } else | |
e30bbc90 AZ |
82 | return -EINVAL; |
83 | ||
2801d36e AZ |
84 | return json_build(ret_v, JSON_BUILD_OBJECT( |
85 | JSON_BUILD_PAIR("mode", JSON_BUILD_STRING(mode)), | |
86 | JSON_BUILD_PAIR("path", JSON_BUILD_STRING(u->cgroup_path)), | |
87 | JSON_BUILD_PAIR("property", JSON_BUILD_STRING(property)), | |
d9d3f05d | 88 | JSON_BUILD_PAIR_CONDITION(use_limit, "limit", JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit)))); |
e30bbc90 AZ |
89 | } |
90 | ||
91 | int manager_varlink_send_managed_oom_update(Unit *u) { | |
92 | _cleanup_(json_variant_unrefp) JsonVariant *arr = NULL, *v = NULL; | |
93 | CGroupContext *c; | |
94 | int r; | |
95 | ||
96 | assert(u); | |
97 | ||
064a5c14 | 98 | if (!UNIT_VTABLE(u)->can_set_managed_oom || !u->manager || !u->cgroup_path) |
e30bbc90 AZ |
99 | return 0; |
100 | ||
064a5c14 DDM |
101 | if (MANAGER_IS_SYSTEM(u->manager)) { |
102 | /* In system mode we can't send any notifications unless oomd connected back to us. In this | |
103 | * mode oomd must initiate communication, not us. */ | |
104 | if (!u->manager->managed_oom_varlink) | |
105 | return 0; | |
106 | } else { | |
107 | /* If we are in user mode, let's connect to oomd if we aren't connected yet. In this mode we | |
108 | * must initiate communication to oomd, not the other way round. */ | |
109 | r = manager_varlink_init(u->manager); | |
110 | if (r <= 0) | |
111 | return r; | |
112 | } | |
113 | ||
e30bbc90 AZ |
114 | c = unit_get_cgroup_context(u); |
115 | if (!c) | |
116 | return 0; | |
117 | ||
118 | r = json_build(&arr, JSON_BUILD_EMPTY_ARRAY); | |
119 | if (r < 0) | |
120 | return r; | |
121 | ||
122 | for (size_t i = 0; i < ELEMENTSOF(managed_oom_mode_properties); i++) { | |
123 | _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; | |
124 | ||
125 | r = build_managed_oom_json_array_element(u, managed_oom_mode_properties[i], &e); | |
126 | if (r < 0) | |
127 | return r; | |
128 | ||
129 | r = json_variant_append_array(&arr, e); | |
130 | if (r < 0) | |
131 | return r; | |
132 | } | |
133 | ||
134 | r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("cgroups", JSON_BUILD_VARIANT(arr)))); | |
135 | if (r < 0) | |
136 | return r; | |
137 | ||
064a5c14 DDM |
138 | if (MANAGER_IS_SYSTEM(u->manager)) |
139 | /* in system mode, oomd is our client, thus send out notifications as replies to the | |
140 | * initiating method call from them. */ | |
141 | r = varlink_notify(u->manager->managed_oom_varlink, v); | |
142 | else | |
143 | /* in user mode, we are oomd's client, thus send out notifications as method calls that do | |
144 | * not expect a reply. */ | |
145 | r = varlink_send(u->manager->managed_oom_varlink, "io.systemd.oom.ReportManagedOOMCGroups", v); | |
146 | ||
147 | return r; | |
e30bbc90 AZ |
148 | } |
149 | ||
f2ed82d5 | 150 | static int build_managed_oom_cgroups_json(Manager *m, JsonVariant **ret) { |
e30bbc90 | 151 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *arr = NULL; |
e30bbc90 AZ |
152 | int r; |
153 | ||
e30bbc90 | 154 | assert(m); |
f2ed82d5 | 155 | assert(ret); |
e30bbc90 AZ |
156 | |
157 | r = json_build(&arr, JSON_BUILD_EMPTY_ARRAY); | |
158 | if (r < 0) | |
159 | return r; | |
160 | ||
56d83b74 LP |
161 | for (UnitType t = 0; t < _UNIT_TYPE_MAX; t++) { |
162 | ||
163 | if (!unit_vtable[t]->can_set_managed_oom) | |
164 | continue; | |
165 | ||
166 | LIST_FOREACH(units_by_type, u, m->units_by_type[t]) { | |
e30bbc90 AZ |
167 | CGroupContext *c; |
168 | ||
169 | if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u))) | |
170 | continue; | |
171 | ||
172 | c = unit_get_cgroup_context(u); | |
173 | if (!c) | |
174 | continue; | |
175 | ||
176 | for (size_t j = 0; j < ELEMENTSOF(managed_oom_mode_properties); j++) { | |
177 | _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; | |
178 | ||
179 | /* For the initial varlink call we only care about units that enabled (i.e. mode is not | |
180 | * set to "auto") oomd properties. */ | |
181 | if (!(streq(managed_oom_mode_properties[j], "ManagedOOMSwap") && c->moom_swap == MANAGED_OOM_KILL) && | |
182 | !(streq(managed_oom_mode_properties[j], "ManagedOOMMemoryPressure") && c->moom_mem_pressure == MANAGED_OOM_KILL)) | |
183 | continue; | |
184 | ||
185 | r = build_managed_oom_json_array_element(u, managed_oom_mode_properties[j], &e); | |
186 | if (r < 0) | |
187 | return r; | |
188 | ||
189 | r = json_variant_append_array(&arr, e); | |
190 | if (r < 0) | |
191 | return r; | |
192 | } | |
193 | } | |
56d83b74 | 194 | } |
e30bbc90 AZ |
195 | |
196 | r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("cgroups", JSON_BUILD_VARIANT(arr)))); | |
197 | if (r < 0) | |
198 | return r; | |
199 | ||
f2ed82d5 DDM |
200 | *ret = TAKE_PTR(v); |
201 | return 0; | |
202 | } | |
203 | ||
204 | static int vl_method_subscribe_managed_oom_cgroups( | |
205 | Varlink *link, | |
206 | JsonVariant *parameters, | |
207 | VarlinkMethodFlags flags, | |
208 | void *userdata) { | |
209 | ||
210 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
99534007 | 211 | Manager *m = ASSERT_PTR(userdata); |
28421289 AZ |
212 | pid_t pid; |
213 | Unit *u; | |
f2ed82d5 DDM |
214 | int r; |
215 | ||
216 | assert(link); | |
f2ed82d5 | 217 | |
28421289 AZ |
218 | r = varlink_get_peer_pid(link, &pid); |
219 | if (r < 0) | |
220 | return r; | |
221 | ||
222 | u = manager_get_unit_by_pid(m, pid); | |
223 | if (!u) | |
224 | return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL); | |
225 | ||
226 | /* This is meant to be a deterrent and not actual security. The alternative is to check for the systemd-oom | |
227 | * user that this unit runs as, but NSS lookups are blocking and not allowed from PID 1. */ | |
228 | if (!streq(u->id, "systemd-oomd.service")) | |
229 | return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL); | |
230 | ||
f2ed82d5 DDM |
231 | if (json_variant_elements(parameters) > 0) |
232 | return varlink_error_invalid_parameter(link, parameters); | |
233 | ||
234 | /* We only take one subscriber for this method so return an error if there's already an existing one. | |
235 | * This shouldn't happen since systemd-oomd is the only client of this method. */ | |
064a5c14 | 236 | if (FLAGS_SET(flags, VARLINK_METHOD_MORE) && m->managed_oom_varlink) |
7dce9ead | 237 | return varlink_error(link, "io.systemd.ManagedOOM.SubscriptionTaken", NULL); |
f2ed82d5 DDM |
238 | |
239 | r = build_managed_oom_cgroups_json(m, &v); | |
240 | if (r < 0) | |
241 | return r; | |
242 | ||
e30bbc90 AZ |
243 | if (!FLAGS_SET(flags, VARLINK_METHOD_MORE)) |
244 | return varlink_reply(link, v); | |
245 | ||
064a5c14 DDM |
246 | assert(!m->managed_oom_varlink); |
247 | m->managed_oom_varlink = varlink_ref(link); | |
248 | return varlink_notify(m->managed_oom_varlink, v); | |
249 | } | |
250 | ||
251 | static int manager_varlink_send_managed_oom_initial(Manager *m) { | |
252 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
253 | int r; | |
254 | ||
255 | assert(m); | |
256 | ||
257 | if (MANAGER_IS_SYSTEM(m)) | |
258 | return 0; | |
259 | ||
260 | assert(m->managed_oom_varlink); | |
261 | ||
262 | r = build_managed_oom_cgroups_json(m, &v); | |
263 | if (r < 0) | |
264 | return r; | |
265 | ||
266 | return varlink_send(m->managed_oom_varlink, "io.systemd.oom.ReportManagedOOMCGroups", v); | |
e30bbc90 AZ |
267 | } |
268 | ||
19d22d43 LP |
269 | static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { |
270 | ||
271 | static const JsonDispatch dispatch_table[] = { | |
272 | { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, | |
273 | { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, | |
274 | { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, | |
275 | {} | |
276 | }; | |
277 | ||
278 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
279 | LookupParameters p = { | |
280 | .uid = UID_INVALID, | |
281 | }; | |
282 | _cleanup_free_ char *found_name = NULL; | |
283 | uid_t found_uid = UID_INVALID, uid; | |
99534007 | 284 | Manager *m = ASSERT_PTR(userdata); |
19d22d43 LP |
285 | const char *un; |
286 | int r; | |
287 | ||
288 | assert(parameters); | |
19d22d43 | 289 | |
f1b622a0 LP |
290 | r = varlink_dispatch(link, parameters, dispatch_table, &p); |
291 | if (r != 0) | |
19d22d43 LP |
292 | return r; |
293 | ||
294 | if (!streq_ptr(p.service, "io.systemd.DynamicUser")) | |
295 | return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); | |
296 | ||
297 | if (uid_is_valid(p.uid)) | |
298 | r = dynamic_user_lookup_uid(m, p.uid, &found_name); | |
299 | else if (p.user_name) | |
300 | r = dynamic_user_lookup_name(m, p.user_name, &found_uid); | |
301 | else { | |
19d22d43 LP |
302 | DynamicUser *d; |
303 | ||
90e74a66 | 304 | HASHMAP_FOREACH(d, m->dynamic_users) { |
19d22d43 LP |
305 | r = dynamic_user_current(d, &uid); |
306 | if (r == -EAGAIN) /* not realized yet? */ | |
307 | continue; | |
308 | if (r < 0) | |
309 | return r; | |
310 | ||
311 | if (!user_match_lookup_parameters(&p, d->name, uid)) | |
312 | continue; | |
313 | ||
314 | if (v) { | |
315 | r = varlink_notify(link, v); | |
316 | if (r < 0) | |
317 | return r; | |
318 | ||
319 | v = json_variant_unref(v); | |
320 | } | |
321 | ||
322 | r = build_user_json(d->name, uid, &v); | |
323 | if (r < 0) | |
324 | return r; | |
325 | } | |
326 | ||
327 | if (!v) | |
328 | return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); | |
329 | ||
330 | return varlink_reply(link, v); | |
331 | } | |
332 | if (r == -ESRCH) | |
333 | return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); | |
334 | if (r < 0) | |
335 | return r; | |
336 | ||
337 | uid = uid_is_valid(found_uid) ? found_uid : p.uid; | |
338 | un = found_name ?: p.user_name; | |
339 | ||
340 | if (!user_match_lookup_parameters(&p, un, uid)) | |
341 | return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL); | |
342 | ||
343 | r = build_user_json(un, uid, &v); | |
344 | if (r < 0) | |
345 | return r; | |
346 | ||
347 | return varlink_reply(link, v); | |
348 | } | |
349 | ||
350 | static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret) { | |
351 | assert(group_name); | |
352 | assert(gid_is_valid(gid)); | |
353 | assert(ret); | |
354 | ||
355 | return json_build(ret, JSON_BUILD_OBJECT( | |
4ca739e2 ZJS |
356 | JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT( |
357 | JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)), | |
0cdf6b14 | 358 | JSON_BUILD_PAIR("description", JSON_BUILD_CONST_STRING("Dynamic Group")), |
4ca739e2 | 359 | JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)), |
0cdf6b14 LP |
360 | JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.DynamicUser")), |
361 | JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("dynamic")))))); | |
4ca739e2 | 362 | } |
19d22d43 LP |
363 | |
364 | static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) { | |
365 | assert(p); | |
366 | ||
367 | if (p->group_name && !streq(name, p->group_name)) | |
368 | return false; | |
369 | ||
370 | if (gid_is_valid(p->gid) && gid != p->gid) | |
371 | return false; | |
372 | ||
373 | return true; | |
374 | } | |
375 | ||
376 | static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { | |
377 | ||
378 | static const JsonDispatch dispatch_table[] = { | |
379 | { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, | |
380 | { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, | |
381 | { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, | |
382 | {} | |
383 | }; | |
384 | ||
385 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
386 | LookupParameters p = { | |
387 | .gid = GID_INVALID, | |
388 | }; | |
389 | _cleanup_free_ char *found_name = NULL; | |
390 | uid_t found_gid = GID_INVALID, gid; | |
99534007 | 391 | Manager *m = ASSERT_PTR(userdata); |
19d22d43 LP |
392 | const char *gn; |
393 | int r; | |
394 | ||
395 | assert(parameters); | |
19d22d43 | 396 | |
f1b622a0 LP |
397 | r = varlink_dispatch(link, parameters, dispatch_table, &p); |
398 | if (r != 0) | |
19d22d43 LP |
399 | return r; |
400 | ||
401 | if (!streq_ptr(p.service, "io.systemd.DynamicUser")) | |
402 | return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); | |
403 | ||
404 | if (gid_is_valid(p.gid)) | |
405 | r = dynamic_user_lookup_uid(m, (uid_t) p.gid, &found_name); | |
406 | else if (p.group_name) | |
407 | r = dynamic_user_lookup_name(m, p.group_name, (uid_t*) &found_gid); | |
408 | else { | |
409 | DynamicUser *d; | |
19d22d43 | 410 | |
90e74a66 | 411 | HASHMAP_FOREACH(d, m->dynamic_users) { |
19d22d43 LP |
412 | uid_t uid; |
413 | ||
414 | r = dynamic_user_current(d, &uid); | |
415 | if (r == -EAGAIN) | |
416 | continue; | |
417 | if (r < 0) | |
418 | return r; | |
419 | ||
420 | if (!group_match_lookup_parameters(&p, d->name, (gid_t) uid)) | |
421 | continue; | |
422 | ||
423 | if (v) { | |
424 | r = varlink_notify(link, v); | |
425 | if (r < 0) | |
426 | return r; | |
427 | ||
428 | v = json_variant_unref(v); | |
429 | } | |
430 | ||
431 | r = build_group_json(d->name, (gid_t) uid, &v); | |
432 | if (r < 0) | |
433 | return r; | |
434 | } | |
435 | ||
436 | if (!v) | |
437 | return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); | |
438 | ||
439 | return varlink_reply(link, v); | |
440 | } | |
441 | if (r == -ESRCH) | |
442 | return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); | |
443 | if (r < 0) | |
444 | return r; | |
445 | ||
446 | gid = gid_is_valid(found_gid) ? found_gid : p.gid; | |
447 | gn = found_name ?: p.group_name; | |
448 | ||
449 | if (!group_match_lookup_parameters(&p, gn, gid)) | |
450 | return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL); | |
451 | ||
452 | r = build_group_json(gn, gid, &v); | |
453 | if (r < 0) | |
454 | return r; | |
455 | ||
456 | return varlink_reply(link, v); | |
457 | } | |
458 | ||
459 | static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { | |
460 | ||
461 | static const JsonDispatch dispatch_table[] = { | |
462 | { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, | |
463 | { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, | |
464 | { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, | |
465 | {} | |
466 | }; | |
467 | ||
468 | LookupParameters p = {}; | |
469 | int r; | |
470 | ||
471 | assert(parameters); | |
472 | ||
f1b622a0 LP |
473 | r = varlink_dispatch(link, parameters, dispatch_table, &p); |
474 | if (r != 0) | |
19d22d43 LP |
475 | return r; |
476 | ||
477 | if (!streq_ptr(p.service, "io.systemd.DynamicUser")) | |
478 | return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); | |
479 | ||
480 | /* We don't support auxiliary groups with dynamic users. */ | |
481 | return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); | |
482 | } | |
483 | ||
e30bbc90 | 484 | static void vl_disconnect(VarlinkServer *s, Varlink *link, void *userdata) { |
99534007 | 485 | Manager *m = ASSERT_PTR(userdata); |
e30bbc90 | 486 | |
e30bbc90 AZ |
487 | assert(s); |
488 | assert(link); | |
489 | ||
064a5c14 DDM |
490 | if (link == m->managed_oom_varlink) |
491 | m->managed_oom_varlink = varlink_unref(link); | |
e30bbc90 AZ |
492 | } |
493 | ||
064a5c14 | 494 | static int manager_varlink_init_system(Manager *m) { |
19d22d43 LP |
495 | _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; |
496 | int r; | |
497 | ||
498 | assert(m); | |
499 | ||
500 | if (m->varlink_server) | |
064a5c14 | 501 | return 1; |
19d22d43 LP |
502 | |
503 | if (!MANAGER_IS_SYSTEM(m)) | |
504 | return 0; | |
505 | ||
658138f3 | 506 | r = manager_setup_varlink_server(m, &s); |
19d22d43 | 507 | if (r < 0) |
658138f3 | 508 | return log_error_errno(r, "Failed to set up varlink server: %m"); |
e30bbc90 | 509 | |
08d50dea | 510 | if (!MANAGER_IS_TEST_RUN(m)) { |
63e00ccd | 511 | (void) mkdir_p_label("/run/systemd/userdb", 0755); |
19d22d43 | 512 | |
6906c028 MY |
513 | FOREACH_STRING(address, "/run/systemd/userdb/io.systemd.DynamicUser", VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM) { |
514 | if (MANAGER_IS_RELOADING(m)) { | |
515 | /* If manager is reloading, we skip listening on existing addresses, since | |
516 | * the fd should be acquired later through deserialization. */ | |
517 | if (access(address, F_OK) >= 0) | |
518 | continue; | |
519 | if (errno != ENOENT) | |
520 | return log_error_errno(errno, | |
521 | "Failed to check if varlink socket '%s' exists: %m", address); | |
522 | } | |
e30bbc90 | 523 | |
6906c028 MY |
524 | r = varlink_server_listen_address(s, address, 0666); |
525 | if (r < 0) | |
526 | return log_error_errno(r, "Failed to bind to varlink socket '%s': %m", address); | |
527 | } | |
08d50dea | 528 | } |
19d22d43 LP |
529 | |
530 | r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); | |
531 | if (r < 0) | |
532 | return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); | |
533 | ||
534 | m->varlink_server = TAKE_PTR(s); | |
064a5c14 DDM |
535 | return 1; |
536 | } | |
537 | ||
538 | static int vl_reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) { | |
99534007 | 539 | Manager *m = ASSERT_PTR(userdata); |
064a5c14 DDM |
540 | int r; |
541 | ||
064a5c14 DDM |
542 | if (error_id) |
543 | log_debug("varlink systemd-oomd client error: %s", error_id); | |
544 | ||
545 | if (FLAGS_SET(flags, VARLINK_REPLY_ERROR) && FLAGS_SET(flags, VARLINK_REPLY_LOCAL)) { | |
546 | /* Varlink connection was closed, likely because of systemd-oomd restart. Let's try to | |
547 | * reconnect and send the initial ManagedOOM update again. */ | |
548 | ||
549 | m->managed_oom_varlink = varlink_unref(link); | |
550 | ||
551 | log_debug("Reconnecting to %s", VARLINK_ADDR_PATH_MANAGED_OOM_USER); | |
552 | ||
553 | r = manager_varlink_init(m); | |
554 | if (r <= 0) | |
555 | return r; | |
556 | } | |
557 | ||
19d22d43 LP |
558 | return 0; |
559 | } | |
560 | ||
064a5c14 DDM |
561 | static int manager_varlink_init_user(Manager *m) { |
562 | _cleanup_(varlink_close_unrefp) Varlink *link = NULL; | |
563 | int r; | |
564 | ||
565 | assert(m); | |
566 | ||
567 | if (m->managed_oom_varlink) | |
568 | return 1; | |
569 | ||
1d1ba688 LP |
570 | if (MANAGER_IS_TEST_RUN(m)) |
571 | return 0; | |
572 | ||
064a5c14 | 573 | r = varlink_connect_address(&link, VARLINK_ADDR_PATH_MANAGED_OOM_USER); |
fa841994 DL |
574 | if (r < 0) { |
575 | if (r == -ENOENT || ERRNO_IS_DISCONNECT(r)) { | |
576 | log_debug("systemd-oomd varlink unix socket not found, skipping user manager varlink setup"); | |
577 | return 0; | |
578 | } | |
064a5c14 | 579 | return log_error_errno(r, "Failed to connect to %s: %m", VARLINK_ADDR_PATH_MANAGED_OOM_USER); |
fa841994 | 580 | } |
064a5c14 DDM |
581 | |
582 | varlink_set_userdata(link, m); | |
583 | ||
584 | r = varlink_bind_reply(link, vl_reply); | |
585 | if (r < 0) | |
586 | return r; | |
587 | ||
588 | r = varlink_attach_event(link, m->event, SD_EVENT_PRIORITY_NORMAL); | |
589 | if (r < 0) | |
590 | return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); | |
591 | ||
592 | m->managed_oom_varlink = TAKE_PTR(link); | |
593 | ||
594 | /* Queue the initial ManagedOOM update. */ | |
595 | (void) manager_varlink_send_managed_oom_initial(m); | |
596 | ||
597 | return 1; | |
598 | } | |
599 | ||
658138f3 AZ |
600 | int manager_setup_varlink_server(Manager *m, VarlinkServer **ret) { |
601 | _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; | |
602 | int r; | |
603 | ||
604 | assert(m); | |
605 | assert(ret); | |
606 | ||
607 | r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA); | |
608 | if (r < 0) | |
609 | return log_debug_errno(r, "Failed to allocate varlink server object: %m"); | |
610 | ||
611 | varlink_server_set_userdata(s, m); | |
612 | ||
abef4a7b LP |
613 | r = varlink_server_add_interface_many( |
614 | s, | |
615 | &vl_interface_io_systemd_UserDatabase, | |
616 | &vl_interface_io_systemd_ManagedOOM); | |
617 | if (r < 0) | |
618 | return log_error_errno(r, "Failed to add interfaces to varlink server: %m"); | |
619 | ||
658138f3 AZ |
620 | r = varlink_server_bind_method_many( |
621 | s, | |
622 | "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record, | |
623 | "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record, | |
624 | "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships, | |
625 | "io.systemd.ManagedOOM.SubscribeManagedOOMCGroups", vl_method_subscribe_managed_oom_cgroups); | |
626 | if (r < 0) | |
627 | return log_debug_errno(r, "Failed to register varlink methods: %m"); | |
628 | ||
629 | r = varlink_server_bind_disconnect(s, vl_disconnect); | |
630 | if (r < 0) | |
631 | return log_debug_errno(r, "Failed to register varlink disconnect handler: %m"); | |
632 | ||
633 | *ret = TAKE_PTR(s); | |
634 | return 0; | |
635 | } | |
636 | ||
064a5c14 DDM |
637 | int manager_varlink_init(Manager *m) { |
638 | return MANAGER_IS_SYSTEM(m) ? manager_varlink_init_system(m) : manager_varlink_init_user(m); | |
639 | } | |
640 | ||
19d22d43 LP |
641 | void manager_varlink_done(Manager *m) { |
642 | assert(m); | |
643 | ||
d65e974e LP |
644 | /* Explicitly close the varlink connection to oomd. Note we first take the varlink connection out of |
645 | * the manager, and only then disconnect it — in two steps – so that we don't end up accidentally | |
646 | * unreffing it twice. After all, closing the connection might cause the disconnect handler we | |
647 | * installed (vl_disconnect() above) to be called, where we will unref it too. */ | |
064a5c14 | 648 | varlink_close_unref(TAKE_PTR(m->managed_oom_varlink)); |
e30bbc90 | 649 | |
19d22d43 | 650 | m->varlink_server = varlink_server_unref(m->varlink_server); |
064a5c14 | 651 | m->managed_oom_varlink = varlink_close_unref(m->managed_oom_varlink); |
19d22d43 | 652 | } |