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