]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
log: fix order of log_unit_struct() to match other logging calls
[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
LP
30#include "hashmap.h"
31#include "strv.h"
32#include "fileio.h"
33#include "special.h"
fb6becb4 34#include "unit-name.h"
1ee306e1 35#include "machine.h"
c3350683
LP
36#include "bus-util.h"
37#include "bus-error.h"
9444b1f2
LP
38
39Machine* machine_new(Manager *manager, const char *name) {
40 Machine *m;
41
42 assert(manager);
43 assert(name);
44
45 m = new0(Machine, 1);
46 if (!m)
47 return NULL;
48
49 m->name = strdup(name);
50 if (!m->name)
51 goto fail;
52
53 m->state_file = strappend("/run/systemd/machines/", m->name);
54 if (!m->state_file)
55 goto fail;
56
57 if (hashmap_put(manager->machines, m->name, m) < 0)
58 goto fail;
59
60 m->class = _MACHINE_CLASS_INVALID;
61 m->manager = manager;
62
63 return m;
64
65fail:
66 free(m->state_file);
67 free(m->name);
68 free(m);
69
70 return NULL;
71}
72
73void machine_free(Machine *m) {
74 assert(m);
75
76 if (m->in_gc_queue)
71fda00f 77 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2 78
89f7c846
LP
79 if (m->unit) {
80 hashmap_remove(m->manager->machine_units, m->unit);
81 free(m->unit);
9444b1f2
LP
82 }
83
fb6becb4
LP
84 free(m->scope_job);
85
9444b1f2
LP
86 hashmap_remove(m->manager->machines, m->name);
87
d3e84ddb
LP
88 if (m->leader > 0)
89 hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
90
c3350683 91 sd_bus_message_unref(m->create_message);
fb6becb4 92
9444b1f2
LP
93 free(m->name);
94 free(m->state_file);
95 free(m->service);
9444b1f2 96 free(m->root_directory);
9b5ed6fe 97 free(m->netif);
9444b1f2
LP
98 free(m);
99}
100
101int machine_save(Machine *m) {
102 _cleanup_free_ char *temp_path = NULL;
103 _cleanup_fclose_ FILE *f = NULL;
104 int r;
105
106 assert(m);
107 assert(m->state_file);
108
109 if (!m->started)
110 return 0;
111
112 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
113 if (r < 0)
114 goto finish;
115
116 r = fopen_temporary(m->state_file, &f, &temp_path);
117 if (r < 0)
118 goto finish;
119
120 fchmod(fileno(f), 0644);
121
122 fprintf(f,
123 "# This is private data. Do not parse.\n"
124 "NAME=%s\n",
125 m->name);
126
ca5405bb
LP
127 if (m->unit) {
128 _cleanup_free_ char *escaped;
129
130 escaped = cescape(m->unit);
131 if (!escaped) {
132 r = -ENOMEM;
133 goto finish;
134 }
135
136 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 */
137 }
fb6becb4
LP
138
139 if (m->scope_job)
140 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2 141
ca5405bb
LP
142 if (m->service) {
143 _cleanup_free_ char *escaped;
9444b1f2 144
ca5405bb
LP
145 escaped = cescape(m->service);
146 if (!escaped) {
147 r = -ENOMEM;
148 goto finish;
149 }
150 fprintf(f, "SERVICE=%s\n", escaped);
151 }
152
153 if (m->root_directory) {
154 _cleanup_free_ char *escaped;
155
156 escaped = cescape(m->root_directory);
157 if (!escaped) {
158 r = -ENOMEM;
159 goto finish;
160 }
161 fprintf(f, "ROOT=%s\n", escaped);
162 }
9444b1f2
LP
163
164 if (!sd_id128_equal(m->id, SD_ID128_NULL))
165 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
166
167 if (m->leader != 0)
90b2de37 168 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
169
170 if (m->class != _MACHINE_CLASS_INVALID)
171 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
172
173 if (dual_timestamp_is_set(&m->timestamp))
174 fprintf(f,
90b2de37
ZJS
175 "REALTIME="USEC_FMT"\n"
176 "MONOTONIC="USEC_FMT"\n",
177 m->timestamp.realtime,
178 m->timestamp.monotonic);
9444b1f2 179
9b5ed6fe
LP
180 if (m->n_netif > 0) {
181 unsigned i;
182
183 fputs("NETIF=", f);
184
185 for (i = 0; i < m->n_netif; i++) {
186 if (i != 0)
187 fputc(' ', f);
188
189 fprintf(f, "%i", m->netif[i]);
190 }
191
192 fputc('\n', f);
193 }
194
034753ac
LP
195 r = fflush_and_check(f);
196 if (r < 0)
197 goto finish;
9444b1f2 198
034753ac 199 if (rename(temp_path, m->state_file) < 0) {
9444b1f2 200 r = -errno;
034753ac 201 goto finish;
9444b1f2
LP
202 }
203
89f7c846
LP
204 if (m->unit) {
205 char *sl;
206
207 /* Create a symlink from the unit name to the machine
208 * name, so that we can quickly find the machine for
209 * each given unit */
210 sl = strappenda("/run/systemd/machines/unit:", m->unit);
211 symlink(m->name, sl);
212 }
213
9444b1f2 214finish:
034753ac
LP
215 if (r < 0) {
216 if (temp_path)
217 unlink(temp_path);
218
90b2de37 219 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
034753ac 220 }
9444b1f2
LP
221
222 return r;
223}
224
89f7c846
LP
225static void machine_unlink(Machine *m) {
226 assert(m);
227
228 if (m->unit) {
229
230 char *sl;
231
232 sl = strappenda("/run/systemd/machines/unit:", m->unit);
233 unlink(sl);
234 }
235
236 if (m->state_file)
237 unlink(m->state_file);
238}
239
9444b1f2 240int machine_load(Machine *m) {
9b5ed6fe 241 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
9444b1f2
LP
242 int r;
243
244 assert(m);
245
246 r = parse_env_file(m->state_file, NEWLINE,
89f7c846 247 "SCOPE", &m->unit,
fb6becb4 248 "SCOPE_JOB", &m->scope_job,
9444b1f2 249 "SERVICE", &m->service,
9444b1f2
LP
250 "ROOT", &m->root_directory,
251 "ID", &id,
252 "LEADER", &leader,
253 "CLASS", &class,
254 "REALTIME", &realtime,
255 "MONOTONIC", &monotonic,
9b5ed6fe 256 "NETIF", &netif,
9444b1f2
LP
257 NULL);
258 if (r < 0) {
259 if (r == -ENOENT)
260 return 0;
261
262 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
263 return r;
264 }
265
266 if (id)
267 sd_id128_from_string(id, &m->id);
268
269 if (leader)
270 parse_pid(leader, &m->leader);
271
272 if (class) {
273 MachineClass c;
274
275 c = machine_class_from_string(class);
276 if (c >= 0)
277 m->class = c;
278 }
279
280 if (realtime) {
281 unsigned long long l;
282 if (sscanf(realtime, "%llu", &l) > 0)
283 m->timestamp.realtime = l;
284 }
285
286 if (monotonic) {
287 unsigned long long l;
288 if (sscanf(monotonic, "%llu", &l) > 0)
289 m->timestamp.monotonic = l;
290 }
291
9b5ed6fe
LP
292 if (netif) {
293 size_t l, allocated = 0, nr = 0;
a2a5291b 294 const char *word, *state;
9b5ed6fe
LP
295 int *ni = NULL;
296
a2a5291b 297 FOREACH_WORD(word, l, netif, state) {
9b5ed6fe
LP
298 char buf[l+1];
299 int ifi;
300
a2a5291b 301 *(char*) (mempcpy(buf, word, l)) = 0;
9b5ed6fe
LP
302
303 if (safe_atoi(buf, &ifi) < 0)
304 continue;
305 if (ifi <= 0)
306 continue;
307
308 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
309 free(ni);
310 return log_oom();
311 }
312
313 ni[nr++] = ifi;
314 }
315
316 free(m->netif);
317 m->netif = ni;
318 m->n_netif = nr;
319 }
320
9444b1f2
LP
321 return r;
322}
323
c3350683 324static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 325 int r = 0;
9444b1f2
LP
326
327 assert(m);
328
89f7c846 329 if (!m->unit) {
d0af76e6 330 _cleanup_free_ char *escaped = NULL;
39883f62 331 char *scope, *description, *job = NULL;
9444b1f2 332
fb6becb4 333 escaped = unit_name_escape(m->name);
9444b1f2
LP
334 if (!escaped)
335 return log_oom();
336
d0af76e6 337 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 338 if (!scope)
9444b1f2 339 return log_oom();
9444b1f2 340
c3350683 341 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 342
c3350683 343 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 344 if (r < 0) {
c3350683 345 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 346 free(scope);
f2d4f98d 347 return r;
d0af76e6 348 } else {
89f7c846 349 m->unit = scope;
d0af76e6
LP
350
351 free(m->scope_job);
352 m->scope_job = job;
353 }
9444b1f2
LP
354 }
355
89f7c846
LP
356 if (m->unit)
357 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 358
fb6becb4 359 return r;
9444b1f2
LP
360}
361
c3350683 362int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
363 int r;
364
365 assert(m);
366
367 if (m->started)
368 return 0;
369
d3e84ddb
LP
370 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
371 if (r < 0)
372 return r;
373
fb6becb4 374 /* Create cgroup */
c3350683 375 r = machine_start_scope(m, properties, error);
fb6becb4
LP
376 if (r < 0)
377 return r;
378
9444b1f2 379 log_struct(LOG_INFO,
e2cc6eca 380 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
9444b1f2 381 "NAME=%s", m->name,
de0671ee 382 "LEADER="PID_FMT, m->leader,
e2cc6eca 383 LOG_MESSAGE("New machine %s.", m->name),
9444b1f2
LP
384 NULL);
385
9444b1f2
LP
386 if (!dual_timestamp_is_set(&m->timestamp))
387 dual_timestamp_get(&m->timestamp);
388
389 m->started = true;
390
391 /* Save new machine data */
392 machine_save(m);
393
394 machine_send_signal(m, true);
395
396 return 0;
397}
398
fb6becb4 399static int machine_stop_scope(Machine *m) {
c3350683 400 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 401 char *job = NULL;
9444b1f2 402 int r;
9444b1f2
LP
403
404 assert(m);
405
89f7c846 406 if (!m->unit)
fb6becb4 407 return 0;
9444b1f2 408
206e7a5f
LP
409 if (!m->registered) {
410 r = manager_stop_unit(m->manager, m->unit, &error, &job);
411 if (r < 0) {
412 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
413 return r;
414 }
fb6becb4 415 }
9444b1f2 416
fb6becb4
LP
417 free(m->scope_job);
418 m->scope_job = job;
9444b1f2 419
f14aa1f1 420 return 0;
9444b1f2
LP
421}
422
423int machine_stop(Machine *m) {
424 int r = 0, k;
425 assert(m);
426
427 if (m->started)
428 log_struct(LOG_INFO,
e2cc6eca 429 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
9444b1f2 430 "NAME=%s", m->name,
de0671ee 431 "LEADER="PID_FMT, m->leader,
e2cc6eca 432 LOG_MESSAGE("Machine %s terminated.", m->name),
9444b1f2
LP
433 NULL);
434
435 /* Kill cgroup */
fb6becb4 436 k = machine_stop_scope(m);
9444b1f2
LP
437 if (k < 0)
438 r = k;
439
89f7c846 440 machine_unlink(m);
9444b1f2
LP
441 machine_add_to_gc_queue(m);
442
443 if (m->started)
444 machine_send_signal(m, false);
445
446 m->started = false;
447
448 return r;
449}
450
a658cafa 451bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
452 assert(m);
453
454 if (drop_not_started && !m->started)
c3350683 455 return false;
9444b1f2 456
c3350683
LP
457 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
458 return true;
9444b1f2 459
89f7c846 460 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 461 return true;
9444b1f2 462
c3350683 463 return false;
9444b1f2
LP
464}
465
466void machine_add_to_gc_queue(Machine *m) {
467 assert(m);
468
469 if (m->in_gc_queue)
470 return;
471
71fda00f 472 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
473 m->in_gc_queue = true;
474}
475
fb6becb4
LP
476MachineState machine_get_state(Machine *s) {
477 assert(s);
9444b1f2 478
fb6becb4
LP
479 if (s->scope_job)
480 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 481
fb6becb4
LP
482 return MACHINE_RUNNING;
483}
9444b1f2 484
fb6becb4
LP
485int machine_kill(Machine *m, KillWho who, int signo) {
486 assert(m);
9444b1f2 487
89f7c846 488 if (!m->unit)
fb6becb4 489 return -ESRCH;
9444b1f2 490
de58a50e
LP
491 if (who == KILL_LEADER) {
492 /* If we shall simply kill the leader, do so directly */
493
494 if (kill(m->leader, signo) < 0)
495 return -errno;
9d685ca8
ED
496
497 return 0;
de58a50e
LP
498 }
499
500 /* Otherwise make PID 1 do it for us, for the entire cgroup */
501 return manager_kill_unit(m->manager, m->unit, signo, NULL);
9444b1f2
LP
502}
503
504static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
505 [MACHINE_CONTAINER] = "container",
506 [MACHINE_VM] = "vm"
507};
508
509DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
510
511static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
512 [MACHINE_OPENING] = "opening",
513 [MACHINE_RUNNING] = "running",
514 [MACHINE_CLOSING] = "closing"
515};
516
517DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
518
519static const char* const kill_who_table[_KILL_WHO_MAX] = {
520 [KILL_LEADER] = "leader",
521 [KILL_ALL] = "all"
522};
523
524DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);