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