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