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