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