]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
machined: port over to PidRef too
[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 "fs-util.h"
21 #include "hashmap.h"
22 #include "machine-dbus.h"
23 #include "machine.h"
24 #include "mkdir-label.h"
25 #include "parse-util.h"
26 #include "path-util.h"
27 #include "process-util.h"
28 #include "serialize.h"
29 #include "special.h"
30 #include "stdio-util.h"
31 #include "string-table.h"
32 #include "terminal-util.h"
33 #include "tmpfile-util.h"
34 #include "unit-name.h"
35 #include "user-util.h"
36
37 DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);
38
39 int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret) {
40 _cleanup_(machine_freep) Machine *m = NULL;
41 int r;
42
43 assert(manager);
44 assert(class < _MACHINE_CLASS_MAX);
45 assert(name);
46 assert(ret);
47
48 /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
49 * means as much as "we don't know yet", and that we'll figure
50 * it out later when loading the state file. */
51
52 m = new(Machine, 1);
53 if (!m)
54 return -ENOMEM;
55
56 *m = (Machine) {
57 .leader = PIDREF_NULL,
58 };
59
60 m->name = strdup(name);
61 if (!m->name)
62 return -ENOMEM;
63
64 if (class != MACHINE_HOST) {
65 m->state_file = path_join("/run/systemd/machines", m->name);
66 if (!m->state_file)
67 return -ENOMEM;
68 }
69
70 m->class = class;
71
72 r = hashmap_put(manager->machines, m->name, m);
73 if (r < 0)
74 return r;
75
76 m->manager = manager;
77
78 *ret = TAKE_PTR(m);
79 return 0;
80 }
81
82 Machine* machine_free(Machine *m) {
83 if (!m)
84 return NULL;
85
86 while (m->operations)
87 operation_free(m->operations);
88
89 if (m->in_gc_queue)
90 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
91
92 machine_release_unit(m);
93
94 free(m->scope_job);
95
96 (void) hashmap_remove(m->manager->machines, m->name);
97
98 if (m->manager->host_machine == m)
99 m->manager->host_machine = NULL;
100
101 if (pidref_is_set(&m->leader)) {
102 (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
103 pidref_done(&m->leader);
104 }
105
106 sd_bus_message_unref(m->create_message);
107
108 free(m->name);
109 free(m->state_file);
110 free(m->service);
111 free(m->root_directory);
112 free(m->netif);
113 return mfree(m);
114 }
115
116 int machine_save(Machine *m) {
117 _cleanup_(unlink_and_freep) char *temp_path = NULL;
118 _cleanup_fclose_ FILE *f = NULL;
119 int r;
120
121 assert(m);
122
123 if (!m->state_file)
124 return 0;
125
126 if (!m->started)
127 return 0;
128
129 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
130 if (r < 0)
131 goto fail;
132
133 r = fopen_temporary(m->state_file, &f, &temp_path);
134 if (r < 0)
135 goto fail;
136
137 (void) fchmod(fileno(f), 0644);
138
139 fprintf(f,
140 "# This is private data. Do not parse.\n"
141 "NAME=%s\n",
142 m->name);
143
144 if (m->unit) {
145 _cleanup_free_ char *escaped = NULL;
146
147 escaped = cescape(m->unit);
148 if (!escaped) {
149 r = -ENOMEM;
150 goto fail;
151 }
152
153 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 */
154 }
155
156 if (m->scope_job)
157 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
158
159 if (m->service) {
160 _cleanup_free_ char *escaped = NULL;
161
162 escaped = cescape(m->service);
163 if (!escaped) {
164 r = -ENOMEM;
165 goto fail;
166 }
167 fprintf(f, "SERVICE=%s\n", escaped);
168 }
169
170 if (m->root_directory) {
171 _cleanup_free_ char *escaped = NULL;
172
173 escaped = cescape(m->root_directory);
174 if (!escaped) {
175 r = -ENOMEM;
176 goto fail;
177 }
178 fprintf(f, "ROOT=%s\n", escaped);
179 }
180
181 if (!sd_id128_is_null(m->id))
182 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
183
184 if (pidref_is_set(&m->leader))
185 fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
186
187 if (m->class != _MACHINE_CLASS_INVALID)
188 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
189
190 if (dual_timestamp_is_set(&m->timestamp))
191 fprintf(f,
192 "REALTIME="USEC_FMT"\n"
193 "MONOTONIC="USEC_FMT"\n",
194 m->timestamp.realtime,
195 m->timestamp.monotonic);
196
197 if (m->n_netif > 0) {
198 size_t i;
199
200 fputs("NETIF=", f);
201
202 for (i = 0; i < m->n_netif; i++) {
203 if (i != 0)
204 fputc(' ', f);
205
206 fprintf(f, "%i", m->netif[i]);
207 }
208
209 fputc('\n', f);
210 }
211
212 r = fflush_and_check(f);
213 if (r < 0)
214 goto fail;
215
216 if (rename(temp_path, m->state_file) < 0) {
217 r = -errno;
218 goto fail;
219 }
220
221 temp_path = mfree(temp_path);
222
223 if (m->unit) {
224 char *sl;
225
226 /* Create a symlink from the unit name to the machine
227 * name, so that we can quickly find the machine for
228 * each given unit. Ignore error. */
229 sl = strjoina("/run/systemd/machines/unit:", m->unit);
230 (void) symlink(m->name, sl);
231 }
232
233 return 0;
234
235 fail:
236 (void) unlink(m->state_file);
237
238 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
239 }
240
241 static void machine_unlink(Machine *m) {
242 assert(m);
243
244 if (m->unit) {
245 char *sl;
246
247 sl = strjoina("/run/systemd/machines/unit:", m->unit);
248 (void) unlink(sl);
249 }
250
251 if (m->state_file)
252 (void) unlink(m->state_file);
253 }
254
255 int machine_load(Machine *m) {
256 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
257 int r;
258
259 assert(m);
260
261 if (!m->state_file)
262 return 0;
263
264 r = parse_env_file(NULL, m->state_file,
265 "SCOPE", &m->unit,
266 "SCOPE_JOB", &m->scope_job,
267 "SERVICE", &m->service,
268 "ROOT", &m->root_directory,
269 "ID", &id,
270 "LEADER", &leader,
271 "CLASS", &class,
272 "REALTIME", &realtime,
273 "MONOTONIC", &monotonic,
274 "NETIF", &netif);
275 if (r == -ENOENT)
276 return 0;
277 if (r < 0)
278 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
279
280 if (id)
281 (void) sd_id128_from_string(id, &m->id);
282
283 if (leader) {
284 pidref_done(&m->leader);
285 r = pidref_set_pidstr(&m->leader, leader);
286 if (r < 0)
287 log_debug_errno(r, "Failed to set leader PID to '%s', ignoring: %m", leader);
288 }
289
290 if (class) {
291 MachineClass c;
292
293 c = machine_class_from_string(class);
294 if (c >= 0)
295 m->class = c;
296 }
297
298 if (realtime)
299 (void) deserialize_usec(realtime, &m->timestamp.realtime);
300 if (monotonic)
301 (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
302
303 if (netif) {
304 _cleanup_free_ int *ni = NULL;
305 size_t nr = 0;
306 const char *p;
307
308 p = netif;
309 for (;;) {
310 _cleanup_free_ char *word = NULL;
311
312 r = extract_first_word(&p, &word, NULL, 0);
313 if (r == 0)
314 break;
315 if (r == -ENOMEM)
316 return log_oom();
317 if (r < 0) {
318 log_warning_errno(r, "Failed to parse NETIF: %s", netif);
319 break;
320 }
321
322 r = parse_ifindex(word);
323 if (r < 0)
324 continue;
325
326 if (!GREEDY_REALLOC(ni, nr + 1))
327 return log_oom();
328
329 ni[nr++] = r;
330 }
331
332 free_and_replace(m->netif, ni);
333 m->n_netif = nr;
334 }
335
336 return r;
337 }
338
339 static int machine_start_scope(
340 Machine *machine,
341 sd_bus_message *more_properties,
342 sd_bus_error *error) {
343
344 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
345 _cleanup_free_ char *escaped = NULL, *unit = NULL;
346 const char *description;
347 int r;
348
349 assert(machine);
350 assert(pidref_is_set(&machine->leader));
351 assert(!machine->unit);
352
353 escaped = unit_name_escape(machine->name);
354 if (!escaped)
355 return log_oom();
356
357 unit = strjoin("machine-", escaped, ".scope");
358 if (!unit)
359 return log_oom();
360
361 r = bus_message_new_method_call(
362 machine->manager->bus,
363 &m,
364 bus_systemd_mgr,
365 "StartTransientUnit");
366 if (r < 0)
367 return r;
368
369 r = sd_bus_message_append(m, "ss", unit, "fail");
370 if (r < 0)
371 return r;
372
373 r = sd_bus_message_open_container(m, 'a', "(sv)");
374 if (r < 0)
375 return r;
376
377 r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
378 if (r < 0)
379 return r;
380
381 description = strjoina(machine->class == MACHINE_VM ? "Virtual Machine " : "Container ", machine->name);
382 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
383 if (r < 0)
384 return r;
385
386 r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
387 "PIDs", "au", 1, machine->leader.pid,
388 "Delegate", "b", 1,
389 "CollectMode", "s", "inactive-or-failed",
390 "AddRef", "b", 1,
391 "TasksMax", "t", UINT64_C(16384));
392 if (r < 0)
393 return r;
394
395 if (more_properties) {
396 r = sd_bus_message_copy(m, more_properties, true);
397 if (r < 0)
398 return r;
399 }
400
401 r = sd_bus_message_close_container(m);
402 if (r < 0)
403 return r;
404
405 r = sd_bus_message_append(m, "a(sa(sv))", 0);
406 if (r < 0)
407 return r;
408
409 r = sd_bus_call(NULL, m, 0, error, &reply);
410 if (r < 0)
411 return r;
412
413 machine->unit = TAKE_PTR(unit);
414 machine->referenced = true;
415
416 const char *job;
417 r = sd_bus_message_read(reply, "o", &job);
418 if (r < 0)
419 return r;
420
421 return free_and_strdup(&machine->scope_job, job);
422 }
423
424 static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
425 int r;
426
427 assert(m);
428 assert(m->class != MACHINE_HOST);
429
430 if (!m->unit) {
431 r = machine_start_scope(m, properties, error);
432 if (r < 0)
433 return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
434 }
435
436 assert(m->unit);
437 hashmap_put(m->manager->machine_units, m->unit, m);
438
439 return 0;
440 }
441
442 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
443 int r;
444
445 assert(m);
446
447 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
448 return -EOPNOTSUPP;
449
450 if (m->started)
451 return 0;
452
453 r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
454 if (r < 0)
455 return r;
456
457 /* Create cgroup */
458 r = machine_ensure_scope(m, properties, error);
459 if (r < 0)
460 return r;
461
462 log_struct(LOG_INFO,
463 "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
464 "NAME=%s", m->name,
465 "LEADER="PID_FMT, m->leader.pid,
466 LOG_MESSAGE("New machine %s.", m->name));
467
468 if (!dual_timestamp_is_set(&m->timestamp))
469 dual_timestamp_get(&m->timestamp);
470
471 m->started = true;
472
473 /* Save new machine data */
474 machine_save(m);
475
476 machine_send_signal(m, true);
477 (void) manager_enqueue_nscd_cache_flush(m->manager);
478
479 return 0;
480 }
481
482 int machine_stop(Machine *m) {
483 int r;
484
485 assert(m);
486
487 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
488 return -EOPNOTSUPP;
489
490 if (m->unit) {
491 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
492 char *job = NULL;
493
494 r = manager_stop_unit(m->manager, m->unit, &error, &job);
495 if (r < 0)
496 return log_error_errno(r, "Failed to stop machine scope: %s", bus_error_message(&error, r));
497
498 free_and_replace(m->scope_job, job);
499 }
500
501 m->stopping = true;
502
503 machine_save(m);
504 (void) manager_enqueue_nscd_cache_flush(m->manager);
505
506 return 0;
507 }
508
509 int machine_finalize(Machine *m) {
510 assert(m);
511
512 if (m->started) {
513 log_struct(LOG_INFO,
514 "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
515 "NAME=%s", m->name,
516 "LEADER="PID_FMT, m->leader.pid,
517 LOG_MESSAGE("Machine %s terminated.", m->name));
518
519 m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
520 }
521
522 machine_unlink(m);
523 machine_add_to_gc_queue(m);
524
525 if (m->started) {
526 machine_send_signal(m, false);
527 m->started = false;
528 }
529
530 return 0;
531 }
532
533 bool machine_may_gc(Machine *m, bool drop_not_started) {
534 assert(m);
535
536 if (m->class == MACHINE_HOST)
537 return false;
538
539 if (drop_not_started && !m->started)
540 return true;
541
542 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
543 return false;
544
545 if (m->unit && manager_unit_is_active(m->manager, m->unit))
546 return false;
547
548 return true;
549 }
550
551 void machine_add_to_gc_queue(Machine *m) {
552 assert(m);
553
554 if (m->in_gc_queue)
555 return;
556
557 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
558 m->in_gc_queue = true;
559 }
560
561 MachineState machine_get_state(Machine *s) {
562 assert(s);
563
564 if (s->class == MACHINE_HOST)
565 return MACHINE_RUNNING;
566
567 if (s->stopping)
568 return MACHINE_CLOSING;
569
570 if (s->scope_job)
571 return MACHINE_OPENING;
572
573 return MACHINE_RUNNING;
574 }
575
576 int machine_kill(Machine *m, KillWho who, int signo) {
577 assert(m);
578
579 if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
580 return -EOPNOTSUPP;
581
582 if (!m->unit)
583 return -ESRCH;
584
585 if (who == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
586 return pidref_kill(&m->leader, signo);
587
588 /* Otherwise, make PID 1 do it for us, for the entire cgroup */
589 return manager_kill_unit(m->manager, m->unit, signo, NULL);
590 }
591
592 int machine_openpt(Machine *m, int flags, char **ret_slave) {
593 assert(m);
594
595 switch (m->class) {
596
597 case MACHINE_HOST:
598 return openpt_allocate(flags, ret_slave);
599
600 case MACHINE_CONTAINER:
601 if (!pidref_is_set(&m->leader))
602 return -EINVAL;
603
604 return openpt_allocate_in_namespace(m->leader.pid, 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 (!pidref_is_set(&m->leader))
621 return -EINVAL;
622
623 return open_terminal_in_namespace(m->leader.pid, 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.pid);
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.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 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.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 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.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 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);