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