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