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