]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/core-varlink.c
core: maintain a single table with event source priorities
[thirdparty/systemd.git] / src / core / core-varlink.c
CommitLineData
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
11typedef 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
21static const char* const managed_oom_mode_properties[] = {
22 "ManagedOOMSwap",
23 "ManagedOOMMemoryPressure",
24};
25
19d22d43
LP
26static 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
44static 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 56static 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
91int 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 150static 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
204static 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
251static 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
269static 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
350static 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
364static 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
376static 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
459static 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 484static 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 494static 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 529
d42b61d2 530 r = varlink_server_attach_event(s, m->event, EVENT_PRIORITY_IPC);
19d22d43
LP
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
538static 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
561static 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
d42b61d2 588 r = varlink_attach_event(link, m->event, EVENT_PRIORITY_IPC);
064a5c14
DDM
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
600int 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
637int 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
641void 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}