]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
Merge pull request #2049 from evverx/journal-test-dont-run-on-incomplete-setup
[thirdparty/systemd.git] / src / machine / machine.c
CommitLineData
9444b1f2
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
66cb2fde 22#include <errno.h>
9444b1f2
LP
23#include <string.h>
24#include <unistd.h>
9444b1f2 25
c3350683 26#include "sd-messages.h"
fb6becb4 27
b5efdb8a 28#include "alloc-util.h"
66cb2fde
LP
29#include "bus-error.h"
30#include "bus-util.h"
4f5dd394 31#include "escape.h"
cf0fbc49 32#include "extract-word.h"
3ffd4af2 33#include "fd-util.h"
9444b1f2 34#include "fileio.h"
66cb2fde
LP
35#include "formats-util.h"
36#include "hashmap.h"
4f5dd394 37#include "machine-dbus.h"
3ffd4af2 38#include "machine.h"
66cb2fde 39#include "mkdir.h"
6bedfcbb 40#include "parse-util.h"
4a0b58c4 41#include "process-util.h"
9444b1f2 42#include "special.h"
8b43440b 43#include "string-table.h"
66cb2fde 44#include "terminal-util.h"
fb6becb4 45#include "unit-name.h"
66cb2fde 46#include "util.h"
9444b1f2 47
fbe55073 48Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
9444b1f2
LP
49 Machine *m;
50
51 assert(manager);
fbe55073 52 assert(class < _MACHINE_CLASS_MAX);
9444b1f2
LP
53 assert(name);
54
fbe55073
LP
55 /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
56 * means as much as "we don't know yet", and that we'll figure
57 * it out later when loading the state file. */
58
9444b1f2
LP
59 m = new0(Machine, 1);
60 if (!m)
61 return NULL;
62
63 m->name = strdup(name);
64 if (!m->name)
65 goto fail;
66
fbe55073
LP
67 if (class != MACHINE_HOST) {
68 m->state_file = strappend("/run/systemd/machines/", m->name);
69 if (!m->state_file)
70 goto fail;
71 }
72
73 m->class = class;
9444b1f2
LP
74
75 if (hashmap_put(manager->machines, m->name, m) < 0)
76 goto fail;
77
9444b1f2
LP
78 m->manager = manager;
79
80 return m;
81
82fail:
83 free(m->state_file);
84 free(m->name);
85 free(m);
86
87 return NULL;
88}
89
90void machine_free(Machine *m) {
91 assert(m);
92
0370612e
LP
93 while (m->operations)
94 machine_operation_unref(m->operations);
95
9444b1f2 96 if (m->in_gc_queue)
71fda00f 97 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2 98
9b420b3c 99 machine_release_unit(m);
9444b1f2 100
fb6becb4
LP
101 free(m->scope_job);
102
9b420b3c 103 (void) hashmap_remove(m->manager->machines, m->name);
9444b1f2 104
fbe55073
LP
105 if (m->manager->host_machine == m)
106 m->manager->host_machine = NULL;
107
d3e84ddb 108 if (m->leader > 0)
4a0b58c4 109 (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
d3e84ddb 110
c3350683 111 sd_bus_message_unref(m->create_message);
fb6becb4 112
9444b1f2
LP
113 free(m->name);
114 free(m->state_file);
115 free(m->service);
9444b1f2 116 free(m->root_directory);
9b5ed6fe 117 free(m->netif);
9444b1f2
LP
118 free(m);
119}
120
121int machine_save(Machine *m) {
122 _cleanup_free_ char *temp_path = NULL;
123 _cleanup_fclose_ FILE *f = NULL;
124 int r;
125
126 assert(m);
fbe55073
LP
127
128 if (!m->state_file)
129 return 0;
9444b1f2
LP
130
131 if (!m->started)
132 return 0;
133
134 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
135 if (r < 0)
dacd6cee 136 goto fail;
9444b1f2
LP
137
138 r = fopen_temporary(m->state_file, &f, &temp_path);
139 if (r < 0)
dacd6cee 140 goto fail;
9444b1f2 141
dacd6cee 142 (void) fchmod(fileno(f), 0644);
9444b1f2
LP
143
144 fprintf(f,
145 "# This is private data. Do not parse.\n"
146 "NAME=%s\n",
147 m->name);
148
ca5405bb
LP
149 if (m->unit) {
150 _cleanup_free_ char *escaped;
151
152 escaped = cescape(m->unit);
153 if (!escaped) {
154 r = -ENOMEM;
dacd6cee 155 goto fail;
ca5405bb
LP
156 }
157
158 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 */
159 }
fb6becb4
LP
160
161 if (m->scope_job)
162 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2 163
ca5405bb
LP
164 if (m->service) {
165 _cleanup_free_ char *escaped;
9444b1f2 166
ca5405bb
LP
167 escaped = cescape(m->service);
168 if (!escaped) {
169 r = -ENOMEM;
dacd6cee 170 goto fail;
ca5405bb
LP
171 }
172 fprintf(f, "SERVICE=%s\n", escaped);
173 }
174
175 if (m->root_directory) {
176 _cleanup_free_ char *escaped;
177
178 escaped = cescape(m->root_directory);
179 if (!escaped) {
180 r = -ENOMEM;
dacd6cee 181 goto fail;
ca5405bb
LP
182 }
183 fprintf(f, "ROOT=%s\n", escaped);
184 }
9444b1f2
LP
185
186 if (!sd_id128_equal(m->id, SD_ID128_NULL))
187 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
188
189 if (m->leader != 0)
90b2de37 190 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
191
192 if (m->class != _MACHINE_CLASS_INVALID)
193 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
194
195 if (dual_timestamp_is_set(&m->timestamp))
196 fprintf(f,
90b2de37
ZJS
197 "REALTIME="USEC_FMT"\n"
198 "MONOTONIC="USEC_FMT"\n",
199 m->timestamp.realtime,
200 m->timestamp.monotonic);
9444b1f2 201
9b5ed6fe
LP
202 if (m->n_netif > 0) {
203 unsigned i;
204
205 fputs("NETIF=", f);
206
207 for (i = 0; i < m->n_netif; i++) {
208 if (i != 0)
209 fputc(' ', f);
210
211 fprintf(f, "%i", m->netif[i]);
212 }
213
214 fputc('\n', f);
215 }
216
034753ac
LP
217 r = fflush_and_check(f);
218 if (r < 0)
dacd6cee 219 goto fail;
9444b1f2 220
034753ac 221 if (rename(temp_path, m->state_file) < 0) {
9444b1f2 222 r = -errno;
dacd6cee 223 goto fail;
9444b1f2
LP
224 }
225
89f7c846
LP
226 if (m->unit) {
227 char *sl;
228
229 /* Create a symlink from the unit name to the machine
230 * name, so that we can quickly find the machine for
e62d9b81 231 * each given unit. Ignore error. */
63c372cb 232 sl = strjoina("/run/systemd/machines/unit:", m->unit);
e62d9b81 233 (void) symlink(m->name, sl);
89f7c846
LP
234 }
235
dacd6cee 236 return 0;
034753ac 237
dacd6cee
LP
238fail:
239 (void) unlink(m->state_file);
240
241 if (temp_path)
242 (void) unlink(temp_path);
9444b1f2 243
dacd6cee 244 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
9444b1f2
LP
245}
246
89f7c846
LP
247static void machine_unlink(Machine *m) {
248 assert(m);
249
250 if (m->unit) {
251
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
9444b1f2 271 r = parse_env_file(m->state_file, NEWLINE,
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,
9b5ed6fe 281 "NETIF", &netif,
9444b1f2
LP
282 NULL);
283 if (r < 0) {
284 if (r == -ENOENT)
285 return 0;
286
8d3d7072 287 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
9444b1f2
LP
288 }
289
290 if (id)
291 sd_id128_from_string(id, &m->id);
292
293 if (leader)
294 parse_pid(leader, &m->leader);
295
296 if (class) {
297 MachineClass c;
298
299 c = machine_class_from_string(class);
300 if (c >= 0)
301 m->class = c;
302 }
303
304 if (realtime) {
305 unsigned long long l;
306 if (sscanf(realtime, "%llu", &l) > 0)
307 m->timestamp.realtime = l;
308 }
309
310 if (monotonic) {
311 unsigned long long l;
312 if (sscanf(monotonic, "%llu", &l) > 0)
313 m->timestamp.monotonic = l;
314 }
315
9b5ed6fe 316 if (netif) {
75a8fd6a
SS
317 size_t allocated = 0, nr = 0;
318 const char *p;
9b5ed6fe
LP
319 int *ni = NULL;
320
75a8fd6a
SS
321 p = netif;
322 for(;;) {
323 _cleanup_free_ char *word = NULL;
9b5ed6fe
LP
324 int ifi;
325
75a8fd6a 326 r = extract_first_word(&p, &word, NULL, 0);
75a8fd6a
SS
327 if (r == 0)
328 break;
6a37c684 329 if (r == -ENOMEM)
52278ad3 330 return log_oom();
6a37c684 331 if (r < 0) {
52278ad3 332 log_warning_errno(r, "Failed to parse NETIF: %s", netif);
6a37c684 333 break;
52278ad3 334 }
75a8fd6a 335
6ad623a3 336 if (parse_ifindex(word, &ifi) < 0)
9b5ed6fe
LP
337 continue;
338
339 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
340 free(ni);
341 return log_oom();
342 }
343
344 ni[nr++] = ifi;
345 }
346
347 free(m->netif);
348 m->netif = ni;
349 m->n_netif = nr;
350 }
351
9444b1f2
LP
352 return r;
353}
354
c3350683 355static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 356 int r = 0;
9444b1f2
LP
357
358 assert(m);
fbe55073 359 assert(m->class != MACHINE_HOST);
9444b1f2 360
89f7c846 361 if (!m->unit) {
d0af76e6 362 _cleanup_free_ char *escaped = NULL;
39883f62 363 char *scope, *description, *job = NULL;
9444b1f2 364
fb6becb4 365 escaped = unit_name_escape(m->name);
9444b1f2
LP
366 if (!escaped)
367 return log_oom();
368
d0af76e6 369 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 370 if (!scope)
9444b1f2 371 return log_oom();
9444b1f2 372
63c372cb 373 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 374
c3350683 375 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 376 if (r < 0) {
c3350683 377 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 378 free(scope);
f2d4f98d 379 return r;
d0af76e6 380 } else {
89f7c846 381 m->unit = scope;
d0af76e6
LP
382
383 free(m->scope_job);
384 m->scope_job = job;
385 }
9444b1f2
LP
386 }
387
89f7c846
LP
388 if (m->unit)
389 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 390
fb6becb4 391 return r;
9444b1f2
LP
392}
393
c3350683 394int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
395 int r;
396
397 assert(m);
398
fbe55073
LP
399 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
400 return -EOPNOTSUPP;
401
9444b1f2
LP
402 if (m->started)
403 return 0;
404
4a0b58c4 405 r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
d3e84ddb
LP
406 if (r < 0)
407 return r;
408
fb6becb4 409 /* Create cgroup */
c3350683 410 r = machine_start_scope(m, properties, error);
fb6becb4
LP
411 if (r < 0)
412 return r;
413
9444b1f2 414 log_struct(LOG_INFO,
e2cc6eca 415 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
9444b1f2 416 "NAME=%s", m->name,
de0671ee 417 "LEADER="PID_FMT, m->leader,
e2cc6eca 418 LOG_MESSAGE("New machine %s.", m->name),
9444b1f2
LP
419 NULL);
420
9444b1f2
LP
421 if (!dual_timestamp_is_set(&m->timestamp))
422 dual_timestamp_get(&m->timestamp);
423
424 m->started = true;
425
426 /* Save new machine data */
427 machine_save(m);
428
429 machine_send_signal(m, true);
430
431 return 0;
432}
433
fb6becb4 434static int machine_stop_scope(Machine *m) {
c3350683 435 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 436 char *job = NULL;
9444b1f2 437 int r;
9444b1f2
LP
438
439 assert(m);
fbe55073 440 assert(m->class != MACHINE_HOST);
9444b1f2 441
89f7c846 442 if (!m->unit)
fb6becb4 443 return 0;
9444b1f2 444
c00a4c8f
LP
445 r = manager_stop_unit(m->manager, m->unit, &error, &job);
446 if (r < 0) {
447 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
448 return r;
fb6becb4 449 }
9444b1f2 450
fb6becb4
LP
451 free(m->scope_job);
452 m->scope_job = job;
9444b1f2 453
f14aa1f1 454 return 0;
9444b1f2
LP
455}
456
457int machine_stop(Machine *m) {
49f3fffd
LP
458 int r;
459 assert(m);
460
fbe55073
LP
461 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
462 return -EOPNOTSUPP;
463
49f3fffd
LP
464 r = machine_stop_scope(m);
465
466 m->stopping = true;
467
468 machine_save(m);
469
470 return r;
471}
472
473int machine_finalize(Machine *m) {
9444b1f2
LP
474 assert(m);
475
476 if (m->started)
477 log_struct(LOG_INFO,
e2cc6eca 478 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
9444b1f2 479 "NAME=%s", m->name,
de0671ee 480 "LEADER="PID_FMT, m->leader,
e2cc6eca 481 LOG_MESSAGE("Machine %s terminated.", m->name),
9444b1f2
LP
482 NULL);
483
89f7c846 484 machine_unlink(m);
9444b1f2
LP
485 machine_add_to_gc_queue(m);
486
49f3fffd 487 if (m->started) {
9444b1f2 488 machine_send_signal(m, false);
49f3fffd
LP
489 m->started = false;
490 }
9444b1f2 491
49f3fffd 492 return 0;
9444b1f2
LP
493}
494
a658cafa 495bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
496 assert(m);
497
fbe55073
LP
498 if (m->class == MACHINE_HOST)
499 return true;
500
9444b1f2 501 if (drop_not_started && !m->started)
c3350683 502 return false;
9444b1f2 503
c3350683
LP
504 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
505 return true;
9444b1f2 506
89f7c846 507 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 508 return true;
9444b1f2 509
c3350683 510 return false;
9444b1f2
LP
511}
512
513void machine_add_to_gc_queue(Machine *m) {
514 assert(m);
515
516 if (m->in_gc_queue)
517 return;
518
71fda00f 519 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
520 m->in_gc_queue = true;
521}
522
fb6becb4
LP
523MachineState machine_get_state(Machine *s) {
524 assert(s);
9444b1f2 525
fbe55073
LP
526 if (s->class == MACHINE_HOST)
527 return MACHINE_RUNNING;
528
49f3fffd
LP
529 if (s->stopping)
530 return MACHINE_CLOSING;
531
fb6becb4 532 if (s->scope_job)
49f3fffd 533 return MACHINE_OPENING;
9444b1f2 534
fb6becb4
LP
535 return MACHINE_RUNNING;
536}
9444b1f2 537
fb6becb4
LP
538int machine_kill(Machine *m, KillWho who, int signo) {
539 assert(m);
9444b1f2 540
fbe55073
LP
541 if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
542 return -EOPNOTSUPP;
543
89f7c846 544 if (!m->unit)
fb6becb4 545 return -ESRCH;
9444b1f2 546
de58a50e
LP
547 if (who == KILL_LEADER) {
548 /* If we shall simply kill the leader, do so directly */
549
550 if (kill(m->leader, signo) < 0)
551 return -errno;
9d685ca8
ED
552
553 return 0;
de58a50e
LP
554 }
555
b938cb90 556 /* Otherwise, make PID 1 do it for us, for the entire cgroup */
de58a50e 557 return manager_kill_unit(m->manager, m->unit, signo, NULL);
9444b1f2
LP
558}
559
fbe55073
LP
560int machine_openpt(Machine *m, int flags) {
561 assert(m);
562
563 switch (m->class) {
564
5f430ff7
LP
565 case MACHINE_HOST: {
566 int fd;
567
568 fd = posix_openpt(flags);
569 if (fd < 0)
570 return -errno;
571
572 if (unlockpt(fd) < 0)
573 return -errno;
574
575 return fd;
576 }
fbe55073
LP
577
578 case MACHINE_CONTAINER:
579 if (m->leader <= 0)
580 return -EINVAL;
581
582 return openpt_in_namespace(m->leader, flags);
583
584 default:
585 return -EOPNOTSUPP;
586 }
587}
588
40e1f4ea
LP
589int machine_open_terminal(Machine *m, const char *path, int mode) {
590 assert(m);
591
592 switch (m->class) {
593
594 case MACHINE_HOST:
595 return open_terminal(path, mode);
596
597 case MACHINE_CONTAINER:
598 if (m->leader <= 0)
599 return -EINVAL;
600
601 return open_terminal_in_namespace(m->leader, path, mode);
602
603 default:
604 return -EOPNOTSUPP;
605 }
606}
607
0370612e
LP
608MachineOperation *machine_operation_unref(MachineOperation *o) {
609 if (!o)
610 return NULL;
611
612 sd_event_source_unref(o->event_source);
613
614 safe_close(o->errno_fd);
615
616 if (o->pid > 1)
617 (void) kill(o->pid, SIGKILL);
618
619 sd_bus_message_unref(o->message);
620
621 if (o->machine) {
622 LIST_REMOVE(operations, o->machine->operations, o);
623 o->machine->n_operations--;
624 }
625
626 free(o);
627 return NULL;
628}
629
9b420b3c
LP
630void machine_release_unit(Machine *m) {
631 assert(m);
632
633 if (!m->unit)
634 return;
635
636 (void) hashmap_remove(m->manager->machine_units, m->unit);
a1e58e8e 637 m->unit = mfree(m->unit);
9b420b3c
LP
638}
639
9444b1f2
LP
640static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
641 [MACHINE_CONTAINER] = "container",
fbe55073
LP
642 [MACHINE_VM] = "vm",
643 [MACHINE_HOST] = "host",
9444b1f2
LP
644};
645
646DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
647
648static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
649 [MACHINE_OPENING] = "opening",
650 [MACHINE_RUNNING] = "running",
651 [MACHINE_CLOSING] = "closing"
652};
653
654DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
655
656static const char* const kill_who_table[_KILL_WHO_MAX] = {
657 [KILL_LEADER] = "leader",
658 [KILL_ALL] = "all"
659};
660
661DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);