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