]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
Merge pull request #29417 from yuwata/sd-journal-cleanups-for-generic-array-bisect...
[thirdparty/systemd.git] / src / machine / machine.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6
7 #include "sd-messages.h"
8
9 #include "alloc-util.h"
10 #include "bus-error.h"
11 #include "bus-locator.h"
12 #include "bus-unit-util.h"
13 #include "bus-util.h"
14 #include "env-file.h"
15 #include "errno-util.h"
16 #include "escape.h"
17 #include "extract-word.h"
18 #include "fd-util.h"
19 #include "fileio.h"
20 #include "format-util.h"
21 #include "fs-util.h"
22 #include "hashmap.h"
23 #include "machine-dbus.h"
24 #include "machine.h"
25 #include "mkdir-label.h"
26 #include "parse-util.h"
27 #include "path-util.h"
28 #include "process-util.h"
29 #include "serialize.h"
30 #include "special.h"
31 #include "stdio-util.h"
32 #include "string-table.h"
33 #include "terminal-util.h"
34 #include "tmpfile-util.h"
35 #include "unit-name.h"
36 #include "user-util.h"
37
38 DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);
39
40 int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret) {
41 _cleanup_(machine_freep) Machine *m = NULL;
42 int r;
43
44 assert(manager);
45 assert(class < _MACHINE_CLASS_MAX);
46 assert(name);
47 assert(ret);
48
49 /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
50 * means as much as "we don't know yet", and that we'll figure
51 * it out later when loading the state file. */
52
53 m = new(Machine, 1);
54 if (!m)
55 return -ENOMEM;
56
57 *m = (Machine) {
58 .leader = PIDREF_NULL,
59 };
60
61 m->name = strdup(name);
62 if (!m->name)
63 return -ENOMEM;
64
65 if (class != MACHINE_HOST) {
66 m->state_file = path_join("/run/systemd/machines", m->name);
67 if (!m->state_file)
68 return -ENOMEM;
69 }
70
71 m->class = class;
72
73 r = hashmap_put(manager->machines, m->name, m);
74 if (r < 0)
75 return r;
76
77 m->manager = manager;
78
79 *ret = TAKE_PTR(m);
80 return 0;
81 }
82
83 Machine* machine_free(Machine *m) {
84 if (!m)
85 return NULL;
86
87 while (m->operations)
88 operation_free(m->operations);
89
90 if (m->in_gc_queue)
91 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
92
93 machine_release_unit(m);
94
95 free(m->scope_job);
96
97 (void) hashmap_remove(m->manager->machines, m->name);
98
99 if (m->manager->host_machine == m)
100 m->manager->host_machine = NULL;
101
102 if (pidref_is_set(&m->leader)) {
103 (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
104 pidref_done(&m->leader);
105 }
106
107 sd_bus_message_unref(m->create_message);
108
109 free(m->name);
110 free(m->state_file);
111 free(m->service);
112 free(m->root_directory);
113 free(m->netif);
114 return mfree(m);
115 }
116
117 int machine_save(Machine *m) {
118 _cleanup_(unlink_and_freep) char *temp_path = NULL;
119 _cleanup_fclose_ FILE *f = NULL;
120 int r;
121
122 assert(m);
123
124 if (!m->state_file)
125 return 0;
126
127 if (!m->started)
128 return 0;
129
130 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
131 if (r < 0)
132 goto fail;
133
134 r = fopen_temporary(m->state_file, &f, &temp_path);
135 if (r < 0)
136 goto fail;
137
138 (void) fchmod(fileno(f), 0644);
139
140 fprintf(f,
141 "# This is private data. Do not parse.\n"
142 "NAME=%s\n",
143 m->name);
144
145 if (m->unit) {
146 _cleanup_free_ char *escaped = NULL;
147
148 escaped = cescape(m->unit);
149 if (!escaped) {
150 r = -ENOMEM;
151 goto fail;
152 }
153
154 fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
155 }
156
157 if (m->scope_job)
158 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
159
160 if (m->service) {
161 _cleanup_free_ char *escaped = NULL;
162
163 escaped = cescape(m->service);
164 if (!escaped) {
165 r = -ENOMEM;
166 goto fail;
167 }
168 fprintf(f, "SERVICE=%s\n", escaped);
169 }
170
171 if (m->root_directory) {
172 _cleanup_free_ char *escaped = NULL;
173
174 escaped = cescape(m->root_directory);
175 if (!escaped) {
176 r = -ENOMEM;
177 goto fail;
178 }
179 fprintf(f, "ROOT=%s\n", escaped);
180 }
181
182 if (!sd_id128_is_null(m->id))
183 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
184
185 if (pidref_is_set(&m->leader))
186 fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
187
188 if (m->class != _MACHINE_CLASS_INVALID)
189 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
190
191 if (dual_timestamp_is_set(&m->timestamp))
192 fprintf(f,
193 "REALTIME="USEC_FMT"\n"
194 "MONOTONIC="USEC_FMT"\n",
195 m->timestamp.realtime,
196 m->timestamp.monotonic);
197
198 if (m->n_netif > 0) {
199 size_t i;
200
201 fputs("NETIF=", f);
202
203 for (i = 0; i < m->n_netif; i++) {
204 if (i != 0)
205 fputc(' ', f);
206
207 fprintf(f, "%i", m->netif[i]);
208 }
209
210 fputc('\n', f);
211 }
212
213 r = fflush_and_check(f);
214 if (r < 0)
215 goto fail;
216
217 if (rename(temp_path, m->state_file) < 0) {
218 r = -errno;
219 goto fail;
220 }
221
222 temp_path = mfree(temp_path);
223
224 if (m->unit) {
225 char *sl;
226
227 /* Create a symlink from the unit name to the machine
228 * name, so that we can quickly find the machine for
229 * each given unit. Ignore error. */
230 sl = strjoina("/run/systemd/machines/unit:", m->unit);
231 (void) symlink(m->name, sl);
232 }
233
234 return 0;
235
236 fail:
237 (void) unlink(m->state_file);
238
239 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
240 }
241
242 static void machine_unlink(Machine *m) {
243 assert(m);
244
245 if (m->unit) {
246 char *sl;
247
248 sl = strjoina("/run/systemd/machines/unit:", m->unit);
249 (void) unlink(sl);
250 }
251
252 if (m->state_file)
253 (void) unlink(m->state_file);
254 }
255
256 int machine_load(Machine *m) {
257 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
258 int r;
259
260 assert(m);
261
262 if (!m->state_file)
263 return 0;
264
265 r = parse_env_file(NULL, m->state_file,
266 "SCOPE", &m->unit,
267 "SCOPE_JOB", &m->scope_job,
268 "SERVICE", &m->service,
269 "ROOT", &m->root_directory,
270 "ID", &id,
271 "LEADER", &leader,
272 "CLASS", &class,
273 "REALTIME", &realtime,
274 "MONOTONIC", &monotonic,
275 "NETIF", &netif);
276 if (r == -ENOENT)
277 return 0;
278 if (r < 0)
279 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
280
281 if (id)
282 (void) sd_id128_from_string(id, &m->id);
283
284 if (leader) {
285 pidref_done(&m->leader);
286 r = pidref_set_pidstr(&m->leader, leader);
287 if (r < 0)
288 log_debug_errno(r, "Failed to set leader PID to '%s', ignoring: %m", leader);
289 }
290
291 if (class) {
292 MachineClass c;
293
294 c = machine_class_from_string(class);
295 if (c >= 0)
296 m->class = c;
297 }
298
299 if (realtime)
300 (void) deserialize_usec(realtime, &m->timestamp.realtime);
301 if (monotonic)
302 (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
303
304 if (netif) {
305 _cleanup_free_ int *ni = NULL;
306 size_t nr = 0;
307 const char *p;
308
309 p = netif;
310 for (;;) {
311 _cleanup_free_ char *word = NULL;
312
313 r = extract_first_word(&p, &word, NULL, 0);
314 if (r == 0)
315 break;
316 if (r == -ENOMEM)
317 return log_oom();
318 if (r < 0) {
319 log_warning_errno(r, "Failed to parse NETIF: %s", netif);
320 break;
321 }
322
323 r = parse_ifindex(word);
324 if (r < 0)
325 continue;
326
327 if (!GREEDY_REALLOC(ni, nr + 1))
328 return log_oom();
329
330 ni[nr++] = r;
331 }
332
333 free_and_replace(m->netif, ni);
334 m->n_netif = nr;
335 }
336
337 return r;
338 }
339
340 static int machine_start_scope(
341 Machine *machine,
342 sd_bus_message *more_properties,
343 sd_bus_error *error) {
344
345 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
346 _cleanup_free_ char *escaped = NULL, *unit = NULL;
347 const char *description;
348 int r;
349
350 assert(machine);
351 assert(pidref_is_set(&machine->leader));
352 assert(!machine->unit);
353
354 escaped = unit_name_escape(machine->name);
355 if (!escaped)
356 return log_oom();
357
358 unit = strjoin("machine-", escaped, ".scope");
359 if (!unit)
360 return log_oom();
361
362 r = bus_message_new_method_call(
363 machine->manager->bus,
364 &m,
365 bus_systemd_mgr,
366 "StartTransientUnit");
367 if (r < 0)
368 return r;
369
370 r = sd_bus_message_append(m, "ss", unit, "fail");
371 if (r < 0)
372 return r;
373
374 r = sd_bus_message_open_container(m, 'a', "(sv)");
375 if (r < 0)
376 return r;
377
378 r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
379 if (r < 0)
380 return r;
381
382 description = strjoina(machine->class == MACHINE_VM ? "Virtual Machine " : "Container ", machine->name);
383 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
384 if (r < 0)
385 return r;
386
387 r = bus_append_scope_pidref(m, &machine->leader);
388 if (r < 0)
389 return r;
390
391 r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
392 "Delegate", "b", 1,
393 "CollectMode", "s", "inactive-or-failed",
394 "AddRef", "b", 1,
395 "TasksMax", "t", UINT64_C(16384));
396 if (r < 0)
397 return r;
398
399 if (more_properties) {
400 r = sd_bus_message_copy(m, more_properties, true);
401 if (r < 0)
402 return r;
403 }
404
405 r = sd_bus_message_close_container(m);
406 if (r < 0)
407 return r;
408
409 r = sd_bus_message_append(m, "a(sa(sv))", 0);
410 if (r < 0)
411 return r;
412
413 r = sd_bus_call(NULL, m, 0, error, &reply);
414 if (r < 0)
415 return r;
416
417 machine->unit = TAKE_PTR(unit);
418 machine->referenced = true;
419
420 const char *job;
421 r = sd_bus_message_read(reply, "o", &job);
422 if (r < 0)
423 return r;
424
425 return free_and_strdup(&machine->scope_job, job);
426 }
427
428 static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
429 int r;
430
431 assert(m);
432 assert(m->class != MACHINE_HOST);
433
434 if (!m->unit) {
435 r = machine_start_scope(m, properties, error);
436 if (r < 0)
437 return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
438 }
439
440 assert(m->unit);
441 hashmap_put(m->manager->machine_units, m->unit, m);
442
443 return 0;
444 }
445
446 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
447 int r;
448
449 assert(m);
450
451 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
452 return -EOPNOTSUPP;
453
454 if (m->started)
455 return 0;
456
457 r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
458 if (r < 0)
459 return r;
460
461 /* Create cgroup */
462 r = machine_ensure_scope(m, properties, error);
463 if (r < 0)
464 return r;
465
466 log_struct(LOG_INFO,
467 "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
468 "NAME=%s", m->name,
469 "LEADER="PID_FMT, m->leader.pid,
470 LOG_MESSAGE("New machine %s.", m->name));
471
472 if (!dual_timestamp_is_set(&m->timestamp))
473 dual_timestamp_get(&m->timestamp);
474
475 m->started = true;
476
477 /* Save new machine data */
478 machine_save(m);
479
480 machine_send_signal(m, true);
481 (void) manager_enqueue_nscd_cache_flush(m->manager);
482
483 return 0;
484 }
485
486 int machine_stop(Machine *m) {
487 int r;
488
489 assert(m);
490
491 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
492 return -EOPNOTSUPP;
493
494 if (m->unit) {
495 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
496 char *job = NULL;
497
498 r = manager_stop_unit(m->manager, m->unit, &error, &job);
499 if (r < 0)
500 return log_error_errno(r, "Failed to stop machine scope: %s", bus_error_message(&error, r));
501
502 free_and_replace(m->scope_job, job);
503 }
504
505 m->stopping = true;
506
507 machine_save(m);
508 (void) manager_enqueue_nscd_cache_flush(m->manager);
509
510 return 0;
511 }
512
513 int machine_finalize(Machine *m) {
514 assert(m);
515
516 if (m->started) {
517 log_struct(LOG_INFO,
518 "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
519 "NAME=%s", m->name,
520 "LEADER="PID_FMT, m->leader.pid,
521 LOG_MESSAGE("Machine %s terminated.", m->name));
522
523 m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
524 }
525
526 machine_unlink(m);
527 machine_add_to_gc_queue(m);
528
529 if (m->started) {
530 machine_send_signal(m, false);
531 m->started = false;
532 }
533
534 return 0;
535 }
536
537 bool machine_may_gc(Machine *m, bool drop_not_started) {
538 assert(m);
539
540 if (m->class == MACHINE_HOST)
541 return false;
542
543 if (drop_not_started && !m->started)
544 return true;
545
546 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
547 return false;
548
549 if (m->unit && manager_unit_is_active(m->manager, m->unit))
550 return false;
551
552 return true;
553 }
554
555 void machine_add_to_gc_queue(Machine *m) {
556 assert(m);
557
558 if (m->in_gc_queue)
559 return;
560
561 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
562 m->in_gc_queue = true;
563 }
564
565 MachineState machine_get_state(Machine *s) {
566 assert(s);
567
568 if (s->class == MACHINE_HOST)
569 return MACHINE_RUNNING;
570
571 if (s->stopping)
572 return MACHINE_CLOSING;
573
574 if (s->scope_job)
575 return MACHINE_OPENING;
576
577 return MACHINE_RUNNING;
578 }
579
580 int machine_kill(Machine *m, KillWho who, int signo) {
581 assert(m);
582
583 if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
584 return -EOPNOTSUPP;
585
586 if (!m->unit)
587 return -ESRCH;
588
589 if (who == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
590 return pidref_kill(&m->leader, signo);
591
592 /* Otherwise, make PID 1 do it for us, for the entire cgroup */
593 return manager_kill_unit(m->manager, m->unit, signo, NULL);
594 }
595
596 int machine_openpt(Machine *m, int flags, char **ret_slave) {
597 assert(m);
598
599 switch (m->class) {
600
601 case MACHINE_HOST:
602 return openpt_allocate(flags, ret_slave);
603
604 case MACHINE_CONTAINER:
605 if (!pidref_is_set(&m->leader))
606 return -EINVAL;
607
608 return openpt_allocate_in_namespace(m->leader.pid, flags, ret_slave);
609
610 default:
611 return -EOPNOTSUPP;
612 }
613 }
614
615 int machine_open_terminal(Machine *m, const char *path, int mode) {
616 assert(m);
617
618 switch (m->class) {
619
620 case MACHINE_HOST:
621 return open_terminal(path, mode);
622
623 case MACHINE_CONTAINER:
624 if (!pidref_is_set(&m->leader))
625 return -EINVAL;
626
627 return open_terminal_in_namespace(m->leader.pid, path, mode);
628
629 default:
630 return -EOPNOTSUPP;
631 }
632 }
633
634 void machine_release_unit(Machine *m) {
635 assert(m);
636
637 if (!m->unit)
638 return;
639
640 if (m->referenced) {
641 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
642 int r;
643
644 r = manager_unref_unit(m->manager, m->unit, &error);
645 if (r < 0)
646 log_warning_errno(r, "Failed to drop reference to machine scope, ignoring: %s",
647 bus_error_message(&error, r));
648
649 m->referenced = false;
650 }
651
652 (void) hashmap_remove(m->manager->machine_units, m->unit);
653 m->unit = mfree(m->unit);
654 }
655
656 int machine_get_uid_shift(Machine *m, uid_t *ret) {
657 char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
658 uid_t uid_base, uid_shift, uid_range;
659 gid_t gid_base, gid_shift, gid_range;
660 _cleanup_fclose_ FILE *f = NULL;
661 int k, r;
662
663 assert(m);
664 assert(ret);
665
666 /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
667 * mappings. In most cases setups should be simple like this, and administrators should only care about the
668 * basic offset a container has relative to the host. This is what this function exposes.
669 *
670 * If we encounter any more complex mappings we politely refuse this with ENXIO. */
671
672 if (m->class == MACHINE_HOST) {
673 *ret = 0;
674 return 0;
675 }
676
677 if (m->class != MACHINE_CONTAINER)
678 return -EOPNOTSUPP;
679
680 xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader.pid);
681 f = fopen(p, "re");
682 if (!f) {
683 if (errno == ENOENT) {
684 /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
685 *ret = 0;
686 return 0;
687 }
688
689 return -errno;
690 }
691
692 /* Read the first line. There's at least one. */
693 errno = 0;
694 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
695 if (k != 3) {
696 if (ferror(f))
697 return errno_or_else(EIO);
698
699 return -EBADMSG;
700 }
701
702 /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
703 if (uid_base != 0)
704 return -ENXIO;
705 /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
706 if (uid_range < UID_NOBODY)
707 return -ENXIO;
708
709 /* If there's more than one line, then we don't support this mapping. */
710 r = safe_fgetc(f, NULL);
711 if (r < 0)
712 return r;
713 if (r != 0) /* Insist on EOF */
714 return -ENXIO;
715
716 fclose(f);
717
718 xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
719 f = fopen(p, "re");
720 if (!f)
721 return -errno;
722
723 /* Read the first line. There's at least one. */
724 errno = 0;
725 k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
726 if (k != 3) {
727 if (ferror(f))
728 return errno_or_else(EIO);
729
730 return -EBADMSG;
731 }
732
733 /* If there's more than one line, then we don't support this file. */
734 r = safe_fgetc(f, NULL);
735 if (r < 0)
736 return r;
737 if (r != 0) /* Insist on EOF */
738 return -ENXIO;
739
740 /* If the UID and GID mapping doesn't match, we don't support this mapping. */
741 if (uid_base != (uid_t) gid_base)
742 return -ENXIO;
743 if (uid_shift != (uid_t) gid_shift)
744 return -ENXIO;
745 if (uid_range != (uid_t) gid_range)
746 return -ENXIO;
747
748 *ret = uid_shift;
749 return 0;
750 }
751
752 static int machine_owns_uid_internal(
753 Machine *machine,
754 const char *map_file, /* "uid_map" or "gid_map" */
755 uid_t uid,
756 uid_t *ret_internal_uid) {
757
758 _cleanup_fclose_ FILE *f = NULL;
759 const char *p;
760
761 /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
762 assert_cc(sizeof(uid_t) == sizeof(gid_t));
763
764 assert(machine);
765
766 /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
767 * internally in the machine */
768
769 if (machine->class != MACHINE_CONTAINER)
770 goto negative;
771
772 p = procfs_file_alloca(machine->leader.pid, map_file);
773 f = fopen(p, "re");
774 if (!f) {
775 log_debug_errno(errno, "Failed to open %s, ignoring.", p);
776 goto negative;
777 }
778
779 for (;;) {
780 uid_t uid_base, uid_shift, uid_range, converted;
781 int k;
782
783 errno = 0;
784 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
785 if (k < 0 && feof(f))
786 break;
787 if (k != 3) {
788 if (ferror(f))
789 return errno_or_else(EIO);
790
791 return -EIO;
792 }
793
794 /* The private user namespace is disabled, ignoring. */
795 if (uid_shift == 0)
796 continue;
797
798 if (uid < uid_shift || uid >= uid_shift + uid_range)
799 continue;
800
801 converted = (uid - uid_shift + uid_base);
802 if (!uid_is_valid(converted))
803 return -EINVAL;
804
805 if (ret_internal_uid)
806 *ret_internal_uid = converted;
807
808 return true;
809 }
810
811 negative:
812 if (ret_internal_uid)
813 *ret_internal_uid = UID_INVALID;
814
815 return false;
816 }
817
818 int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
819 return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
820 }
821
822 int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
823 return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
824 }
825
826 static int machine_translate_uid_internal(
827 Machine *machine,
828 const char *map_file, /* "uid_map" or "gid_map" */
829 uid_t uid,
830 uid_t *ret_host_uid) {
831
832 _cleanup_fclose_ FILE *f = NULL;
833 const char *p;
834
835 /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
836 assert_cc(sizeof(uid_t) == sizeof(gid_t));
837
838 assert(machine);
839 assert(uid_is_valid(uid));
840
841 if (machine->class != MACHINE_CONTAINER)
842 return -ESRCH;
843
844 /* Translates a machine UID into a host UID */
845
846 p = procfs_file_alloca(machine->leader.pid, map_file);
847 f = fopen(p, "re");
848 if (!f)
849 return -errno;
850
851 for (;;) {
852 uid_t uid_base, uid_shift, uid_range, converted;
853 int k;
854
855 errno = 0;
856 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
857 if (k < 0 && feof(f))
858 break;
859 if (k != 3) {
860 if (ferror(f))
861 return errno_or_else(EIO);
862
863 return -EIO;
864 }
865
866 if (uid < uid_base || uid >= uid_base + uid_range)
867 continue;
868
869 converted = uid - uid_base + uid_shift;
870 if (!uid_is_valid(converted))
871 return -EINVAL;
872
873 if (ret_host_uid)
874 *ret_host_uid = converted;
875 return 0;
876 }
877
878 return -ESRCH;
879 }
880
881 int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
882 return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
883 }
884
885 int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
886 return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
887 }
888
889 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
890 [MACHINE_CONTAINER] = "container",
891 [MACHINE_VM] = "vm",
892 [MACHINE_HOST] = "host",
893 };
894
895 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
896
897 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
898 [MACHINE_OPENING] = "opening",
899 [MACHINE_RUNNING] = "running",
900 [MACHINE_CLOSING] = "closing"
901 };
902
903 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
904
905 static const char* const kill_who_table[_KILL_WHO_MAX] = {
906 [KILL_LEADER] = "leader",
907 [KILL_ALL] = "all"
908 };
909
910 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);