]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined-varlink.c
core/scope: drop effectively unused unit_watch_pidref() calls (#38186)
[thirdparty/systemd.git] / src / machine / machined-varlink.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/vm_sockets.h>
4
5 #include "sd-event.h"
6 #include "sd-varlink.h"
7
8 #include "bus-polkit.h"
9 #include "discover-image.h"
10 #include "errno-util.h"
11 #include "format-util.h"
12 #include "hashmap.h"
13 #include "image-policy.h"
14 #include "image-varlink.h"
15 #include "json-util.h"
16 #include "local-addresses.h"
17 #include "machine.h"
18 #include "machine-varlink.h"
19 #include "machined.h"
20 #include "machined-varlink.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "user-util.h"
24 #include "varlink-io.systemd.Machine.h"
25 #include "varlink-io.systemd.MachineImage.h"
26 #include "varlink-io.systemd.UserDatabase.h"
27 #include "varlink-io.systemd.service.h"
28 #include "varlink-util.h"
29
30 typedef struct LookupParameters {
31 const char *user_name;
32 const char *group_name;
33 union {
34 uid_t uid;
35 gid_t gid;
36 };
37 const char *service;
38 } LookupParameters;
39
40 static int build_user_json(const char *user_name, uid_t uid, const char *real_name, sd_json_variant **ret) {
41 assert(user_name);
42 assert(uid_is_valid(uid));
43 assert(ret);
44
45 return sd_json_buildo(
46 ret,
47 SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_OBJECT(
48 SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(user_name)),
49 SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(uid)),
50 SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(GID_NOBODY)),
51 SD_JSON_BUILD_PAIR_CONDITION(!isempty(real_name), "realName", SD_JSON_BUILD_STRING(real_name)),
52 SD_JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/")),
53 SD_JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)),
54 SD_JSON_BUILD_PAIR("locked", SD_JSON_BUILD_BOOLEAN(true)),
55 SD_JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.Machine")),
56 SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("container")))));
57 }
58
59 static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) {
60 assert(p);
61
62 if (p->user_name && !streq(name, p->user_name))
63 return false;
64
65 if (uid_is_valid(p->uid) && uid != p->uid)
66 return false;
67
68 return true;
69 }
70
71 static int user_lookup_uid(Manager *m, uid_t uid, char **ret_name, char **ret_real_name) {
72 _cleanup_free_ char *n = NULL, *rn = NULL;
73 uid_t converted_uid;
74 Machine *machine;
75 int r;
76
77 assert(m);
78 assert(uid_is_valid(uid));
79 assert(ret_name);
80 assert(ret_real_name);
81
82 if (uid < 0x10000) /* Host UID range */
83 return -ESRCH;
84
85 r = manager_find_machine_for_uid(m, uid, &machine, &converted_uid);
86 if (r < 0)
87 return r;
88 if (!r)
89 return -ESRCH;
90
91 if (asprintf(&n, "vu-%s-" UID_FMT, machine->name, converted_uid) < 0)
92 return -ENOMEM;
93
94 /* Don't synthesize invalid user/group names (too long...) */
95 if (!valid_user_group_name(n, 0))
96 return -ESRCH;
97
98 if (asprintf(&rn, "UID " UID_FMT " of Container %s", converted_uid, machine->name) < 0)
99 return -ENOMEM;
100
101 /* Don't synthesize invalid real names either, but since this field doesn't matter much, simply invalidate things */
102 if (!valid_gecos(rn))
103 rn = mfree(rn);
104
105 *ret_name = TAKE_PTR(n);
106 *ret_real_name = TAKE_PTR(rn);
107 return 0;
108 }
109
110 static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char **ret_real_name) {
111 _cleanup_free_ char *mn = NULL, *rn = NULL;
112 uid_t uid, converted_uid;
113 Machine *machine;
114 const char *e, *d;
115 int r;
116
117 assert(m);
118 assert(ret_uid);
119 assert(ret_real_name);
120
121 if (!valid_user_group_name(name, 0))
122 return -ESRCH;
123
124 e = startswith(name, "vu-");
125 if (!e)
126 return -ESRCH;
127
128 d = strrchr(e, '-');
129 if (!d)
130 return -ESRCH;
131
132 if (parse_uid(d + 1, &uid) < 0)
133 return -ESRCH;
134
135 mn = strndup(e, d - e);
136 if (!mn)
137 return -ENOMEM;
138
139 machine = hashmap_get(m->machines, mn);
140 if (!machine)
141 return -ESRCH;
142
143 if (machine->class != MACHINE_CONTAINER)
144 return -ESRCH;
145
146 r = machine_translate_uid(machine, uid, &converted_uid);
147 if (r < 0)
148 return r;
149
150 if (asprintf(&rn, "UID " UID_FMT " of Container %s", uid, machine->name) < 0)
151 return -ENOMEM;
152 if (!valid_gecos(rn))
153 rn = mfree(rn);
154
155 *ret_uid = converted_uid;
156 *ret_real_name = TAKE_PTR(rn);
157 return 0;
158 }
159
160 static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
161
162 static const sd_json_dispatch_field dispatch_table[] = {
163 { "uid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
164 { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), SD_JSON_RELAX },
165 { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
166 {}
167 };
168
169 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
170 LookupParameters p = {
171 .uid = UID_INVALID,
172 };
173 _cleanup_free_ char *found_name = NULL, *found_real_name = NULL;
174 uid_t found_uid = UID_INVALID, uid;
175 Manager *m = ASSERT_PTR(userdata);
176 const char *un;
177 int r;
178
179 assert(parameters);
180
181 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
182 if (r != 0)
183 return r;
184
185 if (!streq_ptr(p.service, "io.systemd.Machine"))
186 return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
187
188 if (uid_is_valid(p.uid))
189 r = user_lookup_uid(m, p.uid, &found_name, &found_real_name);
190 else if (p.user_name)
191 r = user_lookup_name(m, p.user_name, &found_uid, &found_real_name);
192 else
193 return sd_varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
194 if (r == -ESRCH)
195 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
196 if (r < 0)
197 return r;
198
199 uid = uid_is_valid(found_uid) ? found_uid : p.uid;
200 un = found_name ?: p.user_name;
201
202 if (!user_match_lookup_parameters(&p, un, uid))
203 return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
204
205 r = build_user_json(un, uid, found_real_name, &v);
206 if (r < 0)
207 return r;
208
209 return sd_varlink_reply(link, v);
210 }
211
212 static int build_group_json(const char *group_name, gid_t gid, const char *description, sd_json_variant **ret) {
213 assert(group_name);
214 assert(gid_is_valid(gid));
215 assert(ret);
216
217 return sd_json_buildo(
218 ret,
219 SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_OBJECT(
220 SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(group_name)),
221 SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(gid)),
222 SD_JSON_BUILD_PAIR_CONDITION(!isempty(description), "description", SD_JSON_BUILD_STRING(description)),
223 SD_JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.Machine")),
224 SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("container")))));
225 }
226
227 static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) {
228 assert(p);
229
230 if (p->group_name && !streq(name, p->group_name))
231 return false;
232
233 if (gid_is_valid(p->gid) && gid != p->gid)
234 return false;
235
236 return true;
237 }
238
239 static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name, char **ret_description) {
240 _cleanup_free_ char *n = NULL, *d = NULL;
241 gid_t converted_gid;
242 Machine *machine;
243 int r;
244
245 assert(m);
246 assert(gid_is_valid(gid));
247 assert(ret_name);
248 assert(ret_description);
249
250 if (gid < 0x10000) /* Host GID range */
251 return -ESRCH;
252
253 r = manager_find_machine_for_gid(m, gid, &machine, &converted_gid);
254 if (r < 0)
255 return r;
256 if (!r)
257 return -ESRCH;
258
259 if (asprintf(&n, "vg-%s-" GID_FMT, machine->name, converted_gid) < 0)
260 return -ENOMEM;
261
262 if (!valid_user_group_name(n, 0))
263 return -ESRCH;
264
265 if (asprintf(&d, "GID " GID_FMT " of Container %s", converted_gid, machine->name) < 0)
266 return -ENOMEM;
267 if (!valid_gecos(d))
268 d = mfree(d);
269
270 *ret_name = TAKE_PTR(n);
271 *ret_description = TAKE_PTR(d);
272
273 return 0;
274 }
275
276 static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char **ret_description) {
277 _cleanup_free_ char *mn = NULL, *desc = NULL;
278 gid_t gid, converted_gid;
279 Machine *machine;
280 const char *e, *d;
281 int r;
282
283 assert(m);
284 assert(ret_gid);
285 assert(ret_description);
286
287 if (!valid_user_group_name(name, 0))
288 return -ESRCH;
289
290 e = startswith(name, "vg-");
291 if (!e)
292 return -ESRCH;
293
294 d = strrchr(e, '-');
295 if (!d)
296 return -ESRCH;
297
298 if (parse_gid(d + 1, &gid) < 0)
299 return -ESRCH;
300
301 mn = strndup(e, d - e);
302 if (!mn)
303 return -ENOMEM;
304
305 machine = hashmap_get(m->machines, mn);
306 if (!machine)
307 return -ESRCH;
308
309 if (machine->class != MACHINE_CONTAINER)
310 return -ESRCH;
311
312 r = machine_translate_gid(machine, gid, &converted_gid);
313 if (r < 0)
314 return r;
315
316 if (asprintf(&desc, "GID " GID_FMT " of Container %s", gid, machine->name) < 0)
317 return -ENOMEM;
318 if (!valid_gecos(desc))
319 desc = mfree(desc);
320
321 *ret_gid = converted_gid;
322 *ret_description = TAKE_PTR(desc);
323 return 0;
324 }
325
326 static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
327
328 static const sd_json_dispatch_field dispatch_table[] = {
329 { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
330 { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), SD_JSON_RELAX },
331 { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
332 {}
333 };
334
335 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
336 LookupParameters p = {
337 .gid = GID_INVALID,
338 };
339 _cleanup_free_ char *found_name = NULL, *found_description = NULL;
340 uid_t found_gid = GID_INVALID, gid;
341 Manager *m = ASSERT_PTR(userdata);
342 const char *gn;
343 int r;
344
345 assert(parameters);
346
347 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
348 if (r != 0)
349 return r;
350
351 if (!streq_ptr(p.service, "io.systemd.Machine"))
352 return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
353
354 if (gid_is_valid(p.gid))
355 r = group_lookup_gid(m, p.gid, &found_name, &found_description);
356 else if (p.group_name)
357 r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid, &found_description);
358 else
359 return sd_varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
360 if (r == -ESRCH)
361 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
362 if (r < 0)
363 return r;
364
365 gid = gid_is_valid(found_gid) ? found_gid : p.gid;
366 gn = found_name ?: p.group_name;
367
368 if (!group_match_lookup_parameters(&p, gn, gid))
369 return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
370
371 r = build_group_json(gn, gid, found_description, &v);
372 if (r < 0)
373 return r;
374
375 return sd_varlink_reply(link, v);
376 }
377
378 static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
379
380 static const sd_json_dispatch_field dispatch_table[] = {
381 { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), SD_JSON_RELAX },
382 { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), SD_JSON_RELAX },
383 { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
384 {}
385 };
386
387 LookupParameters p = {};
388 int r;
389
390 assert(parameters);
391
392 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
393 if (r != 0)
394 return r;
395
396 if (!streq_ptr(p.service, "io.systemd.Machine"))
397 return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
398
399 /* We don't support auxiliary groups for machines. */
400 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
401 }
402
403 static int json_build_local_addresses(const struct local_address *addresses, size_t n_addresses, sd_json_variant **ret) {
404 _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
405 int r;
406
407 assert(addresses || n_addresses == 0);
408 assert(ret);
409
410 FOREACH_ARRAY(a, addresses, n_addresses) {
411 r = sd_json_variant_append_arraybo(
412 &array,
413 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("ifindex", a->ifindex),
414 SD_JSON_BUILD_PAIR_INTEGER("family", a->family),
415 SD_JSON_BUILD_PAIR_BYTE_ARRAY("address", &a->address.bytes, FAMILY_ADDRESS_SIZE(a->family)));
416 if (r < 0)
417 return r;
418 }
419
420 *ret = TAKE_PTR(array);
421 return 0;
422 }
423
424 static int list_machine_one_and_maybe_read_metadata(sd_varlink *link, Machine *m, bool more, AcquireMetadata am) {
425 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *addr_array = NULL;
426 _cleanup_strv_free_ char **os_release = NULL;
427 uid_t shift = UID_INVALID;
428 int r;
429
430 assert(link);
431 assert(m);
432
433 if (should_acquire_metadata(am)) {
434 _cleanup_free_ struct local_address *addresses = NULL;
435
436 r = machine_get_addresses(m, &addresses);
437 if (r < 0 && am == ACQUIRE_METADATA_GRACEFUL)
438 log_debug_errno(r, "Failed to get address (graceful mode), ignoring: %m");
439 else if (r == -ENONET)
440 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NO_PRIVATE_NETWORKING, NULL);
441 else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
442 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_AVAILABLE, NULL);
443 else if (r < 0)
444 return log_debug_errno(r, "Failed to get addresses: %m");
445 else {
446 r = json_build_local_addresses(addresses, r, &addr_array);
447 if (r < 0)
448 return r;
449 }
450
451 r = machine_get_os_release(m, &os_release);
452 if (r < 0 && am == ACQUIRE_METADATA_GRACEFUL)
453 log_debug_errno(r, "Failed to get OS release (graceful mode), ignoring: %m");
454 else if (r == -ENONET)
455 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NO_OS_RELEASE_INFORMATION, NULL);
456 else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
457 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_AVAILABLE, NULL);
458 else if (r < 0)
459 return log_debug_errno(r, "Failed to get OS release: %m");
460
461 r = machine_get_uid_shift(m, &shift);
462 if (r < 0 && am == ACQUIRE_METADATA_GRACEFUL)
463 log_debug_errno(r, "Failed to get UID shift (graceful mode), ignoring: %m");
464 else if (r == -ENXIO)
465 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NO_UID_SHIFT, NULL);
466 else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
467 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_AVAILABLE, NULL);
468 else if (r < 0)
469 return log_debug_errno(r, "Failed to get UID shift: %m");
470 }
471
472 r = sd_json_buildo(
473 &v,
474 SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(m->name)),
475 SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->id), "id", SD_JSON_BUILD_ID128(m->id)),
476 SD_JSON_BUILD_PAIR("class", SD_JSON_BUILD_STRING(machine_class_to_string(m->class))),
477 JSON_BUILD_PAIR_STRING_NON_EMPTY("service", m->service),
478 JSON_BUILD_PAIR_STRING_NON_EMPTY("rootDirectory", m->root_directory),
479 JSON_BUILD_PAIR_STRING_NON_EMPTY("unit", m->unit),
480 JSON_BUILD_PAIR_STRING_NON_EMPTY("subgroup", m->subgroup),
481 SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->leader), "leader", JSON_BUILD_PIDREF(&m->leader)),
482 SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->supervisor), "supervisor", JSON_BUILD_PIDREF(&m->supervisor)),
483 SD_JSON_BUILD_PAIR_CONDITION(dual_timestamp_is_set(&m->timestamp), "timestamp", JSON_BUILD_DUAL_TIMESTAMP(&m->timestamp)),
484 JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("vSockCid", m->vsock_cid, VMADDR_CID_ANY),
485 JSON_BUILD_PAIR_STRING_NON_EMPTY("sshAddress", m->ssh_address),
486 JSON_BUILD_PAIR_STRING_NON_EMPTY("sshPrivateKeyPath", m->ssh_private_key_path),
487 JSON_BUILD_PAIR_VARIANT_NON_NULL("addresses", addr_array),
488 JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", os_release),
489 JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("UIDShift", shift, UID_INVALID),
490 SD_JSON_BUILD_PAIR_UNSIGNED("UID", m->uid));
491 if (r < 0)
492 return r;
493
494 if (more)
495 return sd_varlink_notify(link, v);
496
497 return sd_varlink_reply(link, v);
498 }
499
500 typedef struct MachineLookupParameters {
501 const char *name;
502 PidRef pidref;
503 AcquireMetadata acquire_metadata;
504 } MachineLookupParameters;
505
506 static void machine_lookup_parameters_done(MachineLookupParameters *p) {
507 assert(p);
508
509 pidref_done(&p->pidref);
510 }
511
512 static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_acquire_metadata, AcquireMetadata, acquire_metadata_from_string);
513
514 static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
515 static const sd_json_dispatch_field dispatch_table[] = {
516 VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineLookupParameters),
517 { "acquireMetadata", SD_JSON_VARIANT_STRING, json_dispatch_acquire_metadata, offsetof(MachineLookupParameters, acquire_metadata), 0 },
518 VARLINK_DISPATCH_POLKIT_FIELD,
519 {}
520 };
521
522 Manager *m = ASSERT_PTR(userdata);
523 _cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = {
524 .pidref = PIDREF_NULL,
525 };
526
527 Machine *machine;
528 int r;
529
530 assert(link);
531 assert(parameters);
532
533 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
534 if (r != 0)
535 return r;
536
537 if (p.name || pidref_is_set(&p.pidref) || pidref_is_automatic(&p.pidref)) {
538 r = lookup_machine_by_name_or_pidref(link, m, p.name, &p.pidref, &machine);
539 if (r == -ESRCH)
540 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NO_SUCH_MACHINE, NULL);
541 if (r < 0)
542 return r;
543
544 return list_machine_one_and_maybe_read_metadata(link, machine, /* more = */ false, p.acquire_metadata);
545 }
546
547 if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
548 return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
549
550 Machine *previous = NULL, *i;
551 HASHMAP_FOREACH(i, m->machines) {
552 if (previous) {
553 r = list_machine_one_and_maybe_read_metadata(link, previous, /* more = */ true, p.acquire_metadata);
554 if (r < 0)
555 return r;
556 }
557
558 previous = i;
559 }
560
561 if (previous)
562 return list_machine_one_and_maybe_read_metadata(link, previous, /* more = */ false, p.acquire_metadata);
563
564 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NO_SUCH_MACHINE, NULL);
565 }
566
567 static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata, sd_varlink_method_t method) {
568 static const sd_json_dispatch_field dispatch_table[] = {
569 VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineLookupParameters),
570 VARLINK_DISPATCH_POLKIT_FIELD,
571 {}
572 };
573
574 Manager *manager = ASSERT_PTR(userdata);
575 _cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = {
576 .pidref = PIDREF_NULL,
577 };
578 Machine *machine;
579 int r;
580
581 assert(link);
582 assert(parameters);
583
584 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
585 if (r != 0)
586 return r;
587
588 r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine);
589 if (r == -ESRCH)
590 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NO_SUCH_MACHINE, NULL);
591 if (r < 0)
592 return r;
593
594 return method(link, parameters, flags, machine);
595 }
596
597 static int vl_method_unregister(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
598 return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_unregister_internal);
599 }
600
601 static int vl_method_terminate(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
602 return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_terminate_internal);
603 }
604
605 static int vl_method_copy_from(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
606 return vl_method_copy_internal(link, parameters, flags, userdata, /* copy_from = */ true);
607 }
608
609 static int vl_method_copy_to(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
610 return vl_method_copy_internal(link, parameters, flags, userdata, /* copy_from = */ false);
611 }
612
613 static int vl_method_open_root_directory(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
614 return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_open_root_directory_internal);
615 }
616
617 static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image, bool more, AcquireMetadata am) {
618 int r;
619
620 assert(link);
621 assert(image);
622
623 if (should_acquire_metadata(am) && !image->metadata_valid) {
624 r = image_read_metadata(image, &image_policy_container);
625 if (r < 0 && am != ACQUIRE_METADATA_GRACEFUL)
626 return log_debug_errno(r, "Failed to read image metadata: %m");
627 if (r < 0)
628 log_debug_errno(r, "Failed to read image metadata (graceful mode), ignoring: %m");
629 }
630
631 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
632
633 r = sd_json_buildo(
634 &v,
635 SD_JSON_BUILD_PAIR_STRING("name", image->name),
636 JSON_BUILD_PAIR_STRING_NON_EMPTY("path", image->path),
637 SD_JSON_BUILD_PAIR_STRING("type", image_type_to_string(image->type)),
638 SD_JSON_BUILD_PAIR_STRING("class", image_class_to_string(image->class)),
639 SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", image->read_only),
640 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("creationTimestamp", image->crtime),
641 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("modificationTimestamp", image->mtime),
642 JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("usage", image->usage, UINT64_MAX),
643 JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("usageExclusive", image->usage_exclusive, UINT64_MAX),
644 JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("limit", image->limit, UINT64_MAX),
645 JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("limitExclusive", image->limit_exclusive, UINT64_MAX));
646 if (r < 0)
647 return r;
648
649 if (should_acquire_metadata(am) && image->metadata_valid) {
650 r = sd_json_variant_merge_objectbo(
651 &v,
652 JSON_BUILD_PAIR_STRING_NON_EMPTY("hostname", image->hostname),
653 SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(image->machine_id), "machineId", SD_JSON_BUILD_ID128(image->machine_id)),
654 JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("machineInfo", image->machine_info),
655 JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", image->os_release));
656 if (r < 0)
657 return r;
658 }
659
660 if (more)
661 return sd_varlink_notify(link, v);
662
663 return sd_varlink_reply(link, v);
664 }
665
666 static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
667 Manager *m = ASSERT_PTR(userdata);
668 struct params {
669 const char *image_name;
670 AcquireMetadata acquire_metadata;
671 } p = {};
672 int r;
673
674 static const sd_json_dispatch_field dispatch_table[] = {
675 { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, image_name), 0 },
676 { "acquireMetadata", SD_JSON_VARIANT_STRING, json_dispatch_acquire_metadata, offsetof(struct params, acquire_metadata), 0 },
677 VARLINK_DISPATCH_POLKIT_FIELD,
678 {}
679 };
680
681 assert(link);
682 assert(parameters);
683
684 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
685 if (r != 0)
686 return r;
687
688 if (p.image_name) {
689 _cleanup_(image_unrefp) Image *found = NULL;
690
691 if (!image_name_is_valid(p.image_name))
692 return sd_varlink_error_invalid_parameter_name(link, "name");
693
694 r = image_find(m->runtime_scope, IMAGE_MACHINE, p.image_name, /* root = */ NULL, &found);
695 if (r == -ENOENT)
696 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_IMAGE_NO_SUCH_IMAGE, NULL);
697 if (r < 0)
698 return log_debug_errno(r, "Failed to find image: %m");
699
700 return list_image_one_and_maybe_read_metadata(link, found, /* more = */ false, p.acquire_metadata);
701 }
702
703 if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
704 return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
705
706 _cleanup_hashmap_free_ Hashmap *images = NULL;
707 r = image_discover(m->runtime_scope, IMAGE_MACHINE, /* root = */ NULL, &images);
708 if (r < 0)
709 return log_debug_errno(r, "Failed to discover images: %m");
710
711 Image *image, *previous = NULL;
712 HASHMAP_FOREACH(image, images) {
713 if (previous) {
714 r = list_image_one_and_maybe_read_metadata(link, previous, /* more = */ true, p.acquire_metadata);
715 if (r < 0)
716 return r;
717 }
718
719 previous = image;
720 }
721
722 if (previous)
723 return list_image_one_and_maybe_read_metadata(link, previous, /* more = */ false, p.acquire_metadata);
724
725 return sd_varlink_error(link, VARLINK_ERROR_MACHINE_IMAGE_NO_SUCH_IMAGE, NULL);
726 }
727
728 static int manager_varlink_init_userdb(Manager *m) {
729 _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
730 int r;
731
732 assert(m);
733
734 if (m->varlink_userdb_server)
735 return 0;
736
737 r = varlink_server_new(&s, SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA, m);
738 if (r < 0)
739 return log_error_errno(r, "Failed to allocate varlink server object: %m");
740
741 r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_UserDatabase);
742 if (r < 0)
743 return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
744
745 r = sd_varlink_server_bind_method_many(
746 s,
747 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
748 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
749 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
750 if (r < 0)
751 return log_error_errno(r, "Failed to register varlink methods: %m");
752
753 r = sd_varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.Machine", 0666 | SD_VARLINK_SERVER_MODE_MKDIR_0755);
754 if (r < 0)
755 return log_error_errno(r, "Failed to bind to varlink socket: %m");
756
757 r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
758 if (r < 0)
759 return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
760
761 m->varlink_userdb_server = TAKE_PTR(s);
762 return 0;
763 }
764
765 static int manager_varlink_init_machine(Manager *m) {
766 _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
767 int r;
768
769 assert(m);
770
771 if (m->varlink_machine_server)
772 return 0;
773
774 r = varlink_server_new(
775 &s,
776 SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|
777 SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT,
778 m);
779 if (r < 0)
780 return log_error_errno(r, "Failed to allocate varlink server object: %m");
781
782 r = sd_varlink_server_add_interface_many(
783 s,
784 &vl_interface_io_systemd_Machine,
785 &vl_interface_io_systemd_MachineImage,
786 &vl_interface_io_systemd_service);
787 if (r < 0)
788 return log_error_errno(r, "Failed to add Machine and MachineImage interfaces to varlink server: %m");
789
790 r = sd_varlink_server_bind_method_many(
791 s,
792 "io.systemd.Machine.Register", vl_method_register,
793 "io.systemd.Machine.List", vl_method_list,
794 "io.systemd.Machine.Unregister", vl_method_unregister,
795 "io.systemd.Machine.Terminate", vl_method_terminate,
796 "io.systemd.Machine.Kill", vl_method_kill,
797 "io.systemd.Machine.Open", vl_method_open,
798 "io.systemd.Machine.OpenRootDirectory", vl_method_open_root_directory,
799 "io.systemd.Machine.MapFrom", vl_method_map_from,
800 "io.systemd.Machine.MapTo", vl_method_map_to,
801 "io.systemd.Machine.BindMount", vl_method_bind_mount,
802 "io.systemd.Machine.CopyFrom", vl_method_copy_from,
803 "io.systemd.Machine.CopyTo", vl_method_copy_to,
804 "io.systemd.MachineImage.List", vl_method_list_images,
805 "io.systemd.MachineImage.Update", vl_method_update_image,
806 "io.systemd.MachineImage.Clone", vl_method_clone_image,
807 "io.systemd.MachineImage.Remove", vl_method_remove_image,
808 "io.systemd.MachineImage.SetPoolLimit", vl_method_set_pool_limit,
809 "io.systemd.MachineImage.CleanPool", vl_method_clean_pool,
810 "io.systemd.service.Ping", varlink_method_ping,
811 "io.systemd.service.SetLogLevel", varlink_method_set_log_level,
812 "io.systemd.service.GetEnvironment", varlink_method_get_environment);
813 if (r < 0)
814 return log_error_errno(r, "Failed to register varlink methods: %m");
815
816 r = sd_varlink_server_listen_auto(s);
817 if (r < 0)
818 return log_error_errno(r, "Failed to bind to passed Varlink sockets: %m");
819 if (r == 0) {
820 r = sd_varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.Machine", 0666 | SD_VARLINK_SERVER_MODE_MKDIR_0755);
821 if (r < 0)
822 return log_error_errno(r, "Failed to bind to io.systemd.Machine varlink socket: %m");
823
824 r = sd_varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.MachineImage", 0666);
825 if (r < 0)
826 return log_error_errno(r, "Failed to bind to io.systemd.MachineImage varlink socket: %m");
827 }
828
829 r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
830 if (r < 0)
831 return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
832
833 m->varlink_machine_server = TAKE_PTR(s);
834 return 0;
835 }
836
837 int manager_varlink_init(Manager *m) {
838 int r;
839
840 r = manager_varlink_init_userdb(m);
841 if (r < 0)
842 return r;
843
844 r = manager_varlink_init_machine(m);
845 if (r < 0)
846 return r;
847
848 return 0;
849 }
850
851 void manager_varlink_done(Manager *m) {
852 assert(m);
853
854 m->varlink_userdb_server = sd_varlink_server_unref(m->varlink_userdb_server);
855 m->varlink_machine_server = sd_varlink_server_unref(m->varlink_machine_server);
856 }