]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
treewide: a few more log_*_errno + return simplifications
[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
da927ba9 219 log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
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
8d3d7072 262 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
9444b1f2
LP
263 }
264
265 if (id)
266 sd_id128_from_string(id, &m->id);
267
268 if (leader)
269 parse_pid(leader, &m->leader);
270
271 if (class) {
272 MachineClass c;
273
274 c = machine_class_from_string(class);
275 if (c >= 0)
276 m->class = c;
277 }
278
279 if (realtime) {
280 unsigned long long l;
281 if (sscanf(realtime, "%llu", &l) > 0)
282 m->timestamp.realtime = l;
283 }
284
285 if (monotonic) {
286 unsigned long long l;
287 if (sscanf(monotonic, "%llu", &l) > 0)
288 m->timestamp.monotonic = l;
289 }
290
9b5ed6fe
LP
291 if (netif) {
292 size_t l, allocated = 0, nr = 0;
a2a5291b 293 const char *word, *state;
9b5ed6fe
LP
294 int *ni = NULL;
295
a2a5291b 296 FOREACH_WORD(word, l, netif, state) {
9b5ed6fe
LP
297 char buf[l+1];
298 int ifi;
299
a2a5291b 300 *(char*) (mempcpy(buf, word, l)) = 0;
9b5ed6fe
LP
301
302 if (safe_atoi(buf, &ifi) < 0)
303 continue;
304 if (ifi <= 0)
305 continue;
306
307 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
308 free(ni);
309 return log_oom();
310 }
311
312 ni[nr++] = ifi;
313 }
314
315 free(m->netif);
316 m->netif = ni;
317 m->n_netif = nr;
318 }
319
9444b1f2
LP
320 return r;
321}
322
c3350683 323static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 324 int r = 0;
9444b1f2
LP
325
326 assert(m);
327
89f7c846 328 if (!m->unit) {
d0af76e6 329 _cleanup_free_ char *escaped = NULL;
39883f62 330 char *scope, *description, *job = NULL;
9444b1f2 331
fb6becb4 332 escaped = unit_name_escape(m->name);
9444b1f2
LP
333 if (!escaped)
334 return log_oom();
335
d0af76e6 336 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 337 if (!scope)
9444b1f2 338 return log_oom();
9444b1f2 339
c3350683 340 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 341
c3350683 342 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 343 if (r < 0) {
c3350683 344 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 345 free(scope);
f2d4f98d 346 return r;
d0af76e6 347 } else {
89f7c846 348 m->unit = scope;
d0af76e6
LP
349
350 free(m->scope_job);
351 m->scope_job = job;
352 }
9444b1f2
LP
353 }
354
89f7c846
LP
355 if (m->unit)
356 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 357
fb6becb4 358 return r;
9444b1f2
LP
359}
360
c3350683 361int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
362 int r;
363
364 assert(m);
365
366 if (m->started)
367 return 0;
368
d3e84ddb
LP
369 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
370 if (r < 0)
371 return r;
372
fb6becb4 373 /* Create cgroup */
c3350683 374 r = machine_start_scope(m, properties, error);
fb6becb4
LP
375 if (r < 0)
376 return r;
377
9444b1f2 378 log_struct(LOG_INFO,
e2cc6eca 379 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
9444b1f2 380 "NAME=%s", m->name,
de0671ee 381 "LEADER="PID_FMT, m->leader,
e2cc6eca 382 LOG_MESSAGE("New machine %s.", m->name),
9444b1f2
LP
383 NULL);
384
9444b1f2
LP
385 if (!dual_timestamp_is_set(&m->timestamp))
386 dual_timestamp_get(&m->timestamp);
387
388 m->started = true;
389
390 /* Save new machine data */
391 machine_save(m);
392
393 machine_send_signal(m, true);
394
395 return 0;
396}
397
fb6becb4 398static int machine_stop_scope(Machine *m) {
c3350683 399 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 400 char *job = NULL;
9444b1f2 401 int r;
9444b1f2
LP
402
403 assert(m);
404
89f7c846 405 if (!m->unit)
fb6becb4 406 return 0;
9444b1f2 407
206e7a5f
LP
408 if (!m->registered) {
409 r = manager_stop_unit(m->manager, m->unit, &error, &job);
410 if (r < 0) {
411 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
412 return r;
413 }
fb6becb4 414 }
9444b1f2 415
fb6becb4
LP
416 free(m->scope_job);
417 m->scope_job = job;
9444b1f2 418
f14aa1f1 419 return 0;
9444b1f2
LP
420}
421
422int machine_stop(Machine *m) {
423 int r = 0, k;
424 assert(m);
425
426 if (m->started)
427 log_struct(LOG_INFO,
e2cc6eca 428 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
9444b1f2 429 "NAME=%s", m->name,
de0671ee 430 "LEADER="PID_FMT, m->leader,
e2cc6eca 431 LOG_MESSAGE("Machine %s terminated.", m->name),
9444b1f2
LP
432 NULL);
433
434 /* Kill cgroup */
fb6becb4 435 k = machine_stop_scope(m);
9444b1f2
LP
436 if (k < 0)
437 r = k;
438
89f7c846 439 machine_unlink(m);
9444b1f2
LP
440 machine_add_to_gc_queue(m);
441
442 if (m->started)
443 machine_send_signal(m, false);
444
445 m->started = false;
446
447 return r;
448}
449
a658cafa 450bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
451 assert(m);
452
453 if (drop_not_started && !m->started)
c3350683 454 return false;
9444b1f2 455
c3350683
LP
456 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
457 return true;
9444b1f2 458
89f7c846 459 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 460 return true;
9444b1f2 461
c3350683 462 return false;
9444b1f2
LP
463}
464
465void machine_add_to_gc_queue(Machine *m) {
466 assert(m);
467
468 if (m->in_gc_queue)
469 return;
470
71fda00f 471 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
472 m->in_gc_queue = true;
473}
474
fb6becb4
LP
475MachineState machine_get_state(Machine *s) {
476 assert(s);
9444b1f2 477
fb6becb4
LP
478 if (s->scope_job)
479 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 480
fb6becb4
LP
481 return MACHINE_RUNNING;
482}
9444b1f2 483
fb6becb4
LP
484int machine_kill(Machine *m, KillWho who, int signo) {
485 assert(m);
9444b1f2 486
89f7c846 487 if (!m->unit)
fb6becb4 488 return -ESRCH;
9444b1f2 489
de58a50e
LP
490 if (who == KILL_LEADER) {
491 /* If we shall simply kill the leader, do so directly */
492
493 if (kill(m->leader, signo) < 0)
494 return -errno;
9d685ca8
ED
495
496 return 0;
de58a50e
LP
497 }
498
499 /* Otherwise make PID 1 do it for us, for the entire cgroup */
500 return manager_kill_unit(m->manager, m->unit, signo, NULL);
9444b1f2
LP
501}
502
503static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
504 [MACHINE_CONTAINER] = "container",
505 [MACHINE_VM] = "vm"
506};
507
508DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
509
510static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
511 [MACHINE_OPENING] = "opening",
512 [MACHINE_RUNNING] = "running",
513 [MACHINE_CLOSING] = "closing"
514};
515
516DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
517
518static const char* const kill_who_table[_KILL_WHO_MAX] = {
519 [KILL_LEADER] = "leader",
520 [KILL_ALL] = "all"
521};
522
523DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);