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