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