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