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