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