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-label.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 
machine_new(Manager * manager,MachineClass class,const char * name)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 
machine_free(Machine * m)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 
machine_save(Machine * m)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 
machine_unlink(Machine * m)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 
machine_load(Machine * m)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 == -ENOENT)
269                 return 0;
270         if (r < 0)
271                 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
272 
273         if (id)
274                 sd_id128_from_string(id, &m->id);
275 
276         if (leader)
277                 parse_pid(leader, &m->leader);
278 
279         if (class) {
280                 MachineClass c;
281 
282                 c = machine_class_from_string(class);
283                 if (c >= 0)
284                         m->class = c;
285         }
286 
287         if (realtime)
288                 (void) deserialize_usec(realtime, &m->timestamp.realtime);
289         if (monotonic)
290                 (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
291 
292         if (netif) {
293                 _cleanup_free_ int *ni = NULL;
294                 size_t nr = 0;
295                 const char *p;
296 
297                 p = netif;
298                 for (;;) {
299                         _cleanup_free_ char *word = NULL;
300 
301                         r = extract_first_word(&p, &word, NULL, 0);
302                         if (r == 0)
303                                 break;
304                         if (r == -ENOMEM)
305                                 return log_oom();
306                         if (r < 0) {
307                                 log_warning_errno(r, "Failed to parse NETIF: %s", netif);
308                                 break;
309                         }
310 
311                         r = parse_ifindex(word);
312                         if (r < 0)
313                                 continue;
314 
315                         if (!GREEDY_REALLOC(ni, nr + 1))
316                                 return log_oom();
317 
318                         ni[nr++] = r;
319                 }
320 
321                 free_and_replace(m->netif, ni);
322                 m->n_netif = nr;
323         }
324 
325         return r;
326 }
327 
machine_start_scope(Machine * machine,sd_bus_message * more_properties,sd_bus_error * error)328 static int machine_start_scope(
329                 Machine *machine,
330                 sd_bus_message *more_properties,
331                 sd_bus_error *error) {
332 
333         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
334         _cleanup_free_ char *escaped = NULL, *unit = NULL;
335         const char *description;
336         int r;
337 
338         assert(machine);
339         assert(machine->leader > 0);
340         assert(!machine->unit);
341 
342         escaped = unit_name_escape(machine->name);
343         if (!escaped)
344                 return log_oom();
345 
346         unit = strjoin("machine-", escaped, ".scope");
347         if (!unit)
348                 return log_oom();
349 
350         r = sd_bus_message_new_method_call(
351                         machine->manager->bus,
352                         &m,
353                         "org.freedesktop.systemd1",
354                         "/org/freedesktop/systemd1",
355                         "org.freedesktop.systemd1.Manager",
356                         "StartTransientUnit");
357         if (r < 0)
358                 return r;
359 
360         r = sd_bus_message_append(m, "ss", unit, "fail");
361         if (r < 0)
362                 return r;
363 
364         r = sd_bus_message_open_container(m, 'a', "(sv)");
365         if (r < 0)
366                 return r;
367 
368         r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
369         if (r < 0)
370                 return r;
371 
372         description = strjoina(machine->class == MACHINE_VM ? "Virtual Machine " : "Container ", machine->name);
373         r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
374         if (r < 0)
375                 return r;
376 
377         r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
378                                   "PIDs", "au", 1, machine->leader,
379                                   "Delegate", "b", 1,
380                                   "CollectMode", "s", "inactive-or-failed",
381                                   "AddRef", "b", 1,
382                                   "TasksMax", "t", UINT64_C(16384));
383         if (r < 0)
384                 return r;
385 
386         if (more_properties) {
387                 r = sd_bus_message_copy(m, more_properties, true);
388                 if (r < 0)
389                         return r;
390         }
391 
392         r = sd_bus_message_close_container(m);
393         if (r < 0)
394                 return r;
395 
396         r = sd_bus_message_append(m, "a(sa(sv))", 0);
397         if (r < 0)
398                 return r;
399 
400         r = sd_bus_call(NULL, m, 0, error, &reply);
401         if (r < 0)
402                 return r;
403 
404         machine->unit = TAKE_PTR(unit);
405         machine->referenced = true;
406 
407         const char *job;
408         r = sd_bus_message_read(reply, "o", &job);
409         if (r < 0)
410                 return r;
411 
412         return free_and_strdup(&machine->scope_job, job);
413 }
414 
machine_ensure_scope(Machine * m,sd_bus_message * properties,sd_bus_error * error)415 static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
416         int r;
417 
418         assert(m);
419         assert(m->class != MACHINE_HOST);
420 
421         if (!m->unit) {
422                 r = machine_start_scope(m, properties, error);
423                 if (r < 0)
424                         return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
425         }
426 
427         assert(m->unit);
428         hashmap_put(m->manager->machine_units, m->unit, m);
429 
430         return 0;
431 }
432 
machine_start(Machine * m,sd_bus_message * properties,sd_bus_error * error)433 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
434         int r;
435 
436         assert(m);
437 
438         if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
439                 return -EOPNOTSUPP;
440 
441         if (m->started)
442                 return 0;
443 
444         r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
445         if (r < 0)
446                 return r;
447 
448         /* Create cgroup */
449         r = machine_ensure_scope(m, properties, error);
450         if (r < 0)
451                 return r;
452 
453         log_struct(LOG_INFO,
454                    "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
455                    "NAME=%s", m->name,
456                    "LEADER="PID_FMT, m->leader,
457                    LOG_MESSAGE("New machine %s.", m->name));
458 
459         if (!dual_timestamp_is_set(&m->timestamp))
460                 dual_timestamp_get(&m->timestamp);
461 
462         m->started = true;
463 
464         /* Save new machine data */
465         machine_save(m);
466 
467         machine_send_signal(m, true);
468         (void) manager_enqueue_nscd_cache_flush(m->manager);
469 
470         return 0;
471 }
472 
machine_stop(Machine * m)473 int machine_stop(Machine *m) {
474         int r;
475 
476         assert(m);
477 
478         if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
479                 return -EOPNOTSUPP;
480 
481         if (m->unit) {
482                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
483                 char *job = NULL;
484 
485                 r = manager_stop_unit(m->manager, m->unit, &error, &job);
486                 if (r < 0)
487                         return log_error_errno(r, "Failed to stop machine scope: %s", bus_error_message(&error, r));
488 
489                 free_and_replace(m->scope_job, job);
490         }
491 
492         m->stopping = true;
493 
494         machine_save(m);
495         (void) manager_enqueue_nscd_cache_flush(m->manager);
496 
497         return 0;
498 }
499 
machine_finalize(Machine * m)500 int machine_finalize(Machine *m) {
501         assert(m);
502 
503         if (m->started) {
504                 log_struct(LOG_INFO,
505                            "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
506                            "NAME=%s", m->name,
507                            "LEADER="PID_FMT, m->leader,
508                            LOG_MESSAGE("Machine %s terminated.", m->name));
509 
510                 m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
511         }
512 
513         machine_unlink(m);
514         machine_add_to_gc_queue(m);
515 
516         if (m->started) {
517                 machine_send_signal(m, false);
518                 m->started = false;
519         }
520 
521         return 0;
522 }
523 
machine_may_gc(Machine * m,bool drop_not_started)524 bool machine_may_gc(Machine *m, bool drop_not_started) {
525         assert(m);
526 
527         if (m->class == MACHINE_HOST)
528                 return false;
529 
530         if (drop_not_started && !m->started)
531                 return true;
532 
533         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
534                 return false;
535 
536         if (m->unit && manager_unit_is_active(m->manager, m->unit))
537                 return false;
538 
539         return true;
540 }
541 
machine_add_to_gc_queue(Machine * m)542 void machine_add_to_gc_queue(Machine *m) {
543         assert(m);
544 
545         if (m->in_gc_queue)
546                 return;
547 
548         LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
549         m->in_gc_queue = true;
550 }
551 
machine_get_state(Machine * s)552 MachineState machine_get_state(Machine *s) {
553         assert(s);
554 
555         if (s->class == MACHINE_HOST)
556                 return MACHINE_RUNNING;
557 
558         if (s->stopping)
559                 return MACHINE_CLOSING;
560 
561         if (s->scope_job)
562                 return MACHINE_OPENING;
563 
564         return MACHINE_RUNNING;
565 }
566 
machine_kill(Machine * m,KillWho who,int signo)567 int machine_kill(Machine *m, KillWho who, int signo) {
568         assert(m);
569 
570         if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
571                 return -EOPNOTSUPP;
572 
573         if (!m->unit)
574                 return -ESRCH;
575 
576         if (who == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
577                 return RET_NERRNO(kill(m->leader, signo));
578 
579         /* Otherwise, make PID 1 do it for us, for the entire cgroup */
580         return manager_kill_unit(m->manager, m->unit, signo, NULL);
581 }
582 
machine_openpt(Machine * m,int flags,char ** ret_slave)583 int machine_openpt(Machine *m, int flags, char **ret_slave) {
584         assert(m);
585 
586         switch (m->class) {
587 
588         case MACHINE_HOST:
589 
590                 return openpt_allocate(flags, ret_slave);
591 
592         case MACHINE_CONTAINER:
593                 if (m->leader <= 0)
594                         return -EINVAL;
595 
596                 return openpt_allocate_in_namespace(m->leader, flags, ret_slave);
597 
598         default:
599                 return -EOPNOTSUPP;
600         }
601 }
602 
machine_open_terminal(Machine * m,const char * path,int mode)603 int machine_open_terminal(Machine *m, const char *path, int mode) {
604         assert(m);
605 
606         switch (m->class) {
607 
608         case MACHINE_HOST:
609                 return open_terminal(path, mode);
610 
611         case MACHINE_CONTAINER:
612                 if (m->leader <= 0)
613                         return -EINVAL;
614 
615                 return open_terminal_in_namespace(m->leader, path, mode);
616 
617         default:
618                 return -EOPNOTSUPP;
619         }
620 }
621 
machine_release_unit(Machine * m)622 void machine_release_unit(Machine *m) {
623         assert(m);
624 
625         if (!m->unit)
626                 return;
627 
628         if (m->referenced) {
629                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
630                 int r;
631 
632                 r = manager_unref_unit(m->manager, m->unit, &error);
633                 if (r < 0)
634                         log_warning_errno(r, "Failed to drop reference to machine scope, ignoring: %s",
635                                           bus_error_message(&error, r));
636 
637                 m->referenced = false;
638         }
639 
640         (void) hashmap_remove(m->manager->machine_units, m->unit);
641         m->unit = mfree(m->unit);
642 }
643 
machine_get_uid_shift(Machine * m,uid_t * ret)644 int machine_get_uid_shift(Machine *m, uid_t *ret) {
645         char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
646         uid_t uid_base, uid_shift, uid_range;
647         gid_t gid_base, gid_shift, gid_range;
648         _cleanup_fclose_ FILE *f = NULL;
649         int k, r;
650 
651         assert(m);
652         assert(ret);
653 
654         /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
655          * mappings. In most cases setups should be simple like this, and administrators should only care about the
656          * basic offset a container has relative to the host. This is what this function exposes.
657          *
658          * If we encounter any more complex mappings we politely refuse this with ENXIO. */
659 
660         if (m->class == MACHINE_HOST) {
661                 *ret = 0;
662                 return 0;
663         }
664 
665         if (m->class != MACHINE_CONTAINER)
666                 return -EOPNOTSUPP;
667 
668         xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
669         f = fopen(p, "re");
670         if (!f) {
671                 if (errno == ENOENT) {
672                         /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
673                         *ret = 0;
674                         return 0;
675                 }
676 
677                 return -errno;
678         }
679 
680         /* Read the first line. There's at least one. */
681         errno = 0;
682         k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
683         if (k != 3) {
684                 if (ferror(f))
685                         return errno_or_else(EIO);
686 
687                 return -EBADMSG;
688         }
689 
690         /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
691         if (uid_base != 0)
692                 return -ENXIO;
693         /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
694         if (uid_range < UID_NOBODY)
695                 return -ENXIO;
696 
697         /* If there's more than one line, then we don't support this mapping. */
698         r = safe_fgetc(f, NULL);
699         if (r < 0)
700                 return r;
701         if (r != 0) /* Insist on EOF */
702                 return -ENXIO;
703 
704         fclose(f);
705 
706         xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
707         f = fopen(p, "re");
708         if (!f)
709                 return -errno;
710 
711         /* Read the first line. There's at least one. */
712         errno = 0;
713         k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
714         if (k != 3) {
715                 if (ferror(f))
716                         return errno_or_else(EIO);
717 
718                 return -EBADMSG;
719         }
720 
721         /* If there's more than one line, then we don't support this file. */
722         r = safe_fgetc(f, NULL);
723         if (r < 0)
724                 return r;
725         if (r != 0) /* Insist on EOF */
726                 return -ENXIO;
727 
728         /* If the UID and GID mapping doesn't match, we don't support this mapping. */
729         if (uid_base != (uid_t) gid_base)
730                 return -ENXIO;
731         if (uid_shift != (uid_t) gid_shift)
732                 return -ENXIO;
733         if (uid_range != (uid_t) gid_range)
734                 return -ENXIO;
735 
736         *ret = uid_shift;
737         return 0;
738 }
739 
machine_owns_uid_internal(Machine * machine,const char * map_file,uid_t uid,uid_t * ret_internal_uid)740 static int machine_owns_uid_internal(
741                 Machine *machine,
742                 const char *map_file, /* "uid_map" or "gid_map" */
743                 uid_t uid,
744                 uid_t *ret_internal_uid) {
745 
746         _cleanup_fclose_ FILE *f = NULL;
747         const char *p;
748 
749         /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
750         assert_cc(sizeof(uid_t) == sizeof(gid_t));
751 
752         assert(machine);
753 
754         /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
755          * internally in the machine */
756 
757         if (machine->class != MACHINE_CONTAINER)
758                 goto negative;
759 
760         p = procfs_file_alloca(machine->leader, map_file);
761         f = fopen(p, "re");
762         if (!f) {
763                 log_debug_errno(errno, "Failed to open %s, ignoring.", p);
764                 goto negative;
765         }
766 
767         for (;;) {
768                 uid_t uid_base, uid_shift, uid_range, converted;
769                 int k;
770 
771                 errno = 0;
772                 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
773                 if (k < 0 && feof(f))
774                         break;
775                 if (k != 3) {
776                         if (ferror(f))
777                                 return errno_or_else(EIO);
778 
779                         return -EIO;
780                 }
781 
782                 /* The private user namespace is disabled, ignoring. */
783                 if (uid_shift == 0)
784                         continue;
785 
786                 if (uid < uid_shift || uid >= uid_shift + uid_range)
787                         continue;
788 
789                 converted = (uid - uid_shift + uid_base);
790                 if (!uid_is_valid(converted))
791                         return -EINVAL;
792 
793                 if (ret_internal_uid)
794                         *ret_internal_uid = converted;
795 
796                 return true;
797         }
798 
799 negative:
800         if (ret_internal_uid)
801                 *ret_internal_uid = UID_INVALID;
802 
803         return false;
804 }
805 
machine_owns_uid(Machine * machine,uid_t uid,uid_t * ret_internal_uid)806 int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
807         return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
808 }
809 
machine_owns_gid(Machine * machine,gid_t gid,gid_t * ret_internal_gid)810 int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
811         return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
812 }
813 
machine_translate_uid_internal(Machine * machine,const char * map_file,uid_t uid,uid_t * ret_host_uid)814 static int machine_translate_uid_internal(
815                 Machine *machine,
816                 const char *map_file, /* "uid_map" or "gid_map" */
817                 uid_t uid,
818                 uid_t *ret_host_uid) {
819 
820         _cleanup_fclose_ FILE *f = NULL;
821         const char *p;
822 
823         /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
824         assert_cc(sizeof(uid_t) == sizeof(gid_t));
825 
826         assert(machine);
827         assert(uid_is_valid(uid));
828 
829         if (machine->class != MACHINE_CONTAINER)
830                 return -ESRCH;
831 
832         /* Translates a machine UID into a host UID */
833 
834         p = procfs_file_alloca(machine->leader, map_file);
835         f = fopen(p, "re");
836         if (!f)
837                 return -errno;
838 
839         for (;;) {
840                 uid_t uid_base, uid_shift, uid_range, converted;
841                 int k;
842 
843                 errno = 0;
844                 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
845                 if (k < 0 && feof(f))
846                         break;
847                 if (k != 3) {
848                         if (ferror(f))
849                                 return errno_or_else(EIO);
850 
851                         return -EIO;
852                 }
853 
854                 if (uid < uid_base || uid >= uid_base + uid_range)
855                         continue;
856 
857                 converted = uid - uid_base + uid_shift;
858                 if (!uid_is_valid(converted))
859                         return -EINVAL;
860 
861                 if (ret_host_uid)
862                         *ret_host_uid = converted;
863                 return 0;
864         }
865 
866         return -ESRCH;
867 }
868 
machine_translate_uid(Machine * machine,gid_t uid,gid_t * ret_host_uid)869 int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
870         return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
871 }
872 
machine_translate_gid(Machine * machine,gid_t gid,gid_t * ret_host_gid)873 int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
874         return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
875 }
876 
877 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
878         [MACHINE_CONTAINER] = "container",
879         [MACHINE_VM] = "vm",
880         [MACHINE_HOST] = "host",
881 };
882 
883 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
884 
885 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
886         [MACHINE_OPENING] = "opening",
887         [MACHINE_RUNNING] = "running",
888         [MACHINE_CLOSING] = "closing"
889 };
890 
891 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
892 
893 static const char* const kill_who_table[_KILL_WHO_MAX] = {
894         [KILL_LEADER] = "leader",
895         [KILL_ALL] = "all"
896 };
897 
898 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
899