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