]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
machined: validate machine names at more places
[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
LP
39
40Machine* machine_new(Manager *manager, const char *name) {
41 Machine *m;
42
43 assert(manager);
44 assert(name);
45
46 m = new0(Machine, 1);
47 if (!m)
48 return NULL;
49
50 m->name = strdup(name);
51 if (!m->name)
52 goto fail;
53
54 m->state_file = strappend("/run/systemd/machines/", m->name);
55 if (!m->state_file)
56 goto fail;
57
58 if (hashmap_put(manager->machines, m->name, m) < 0)
59 goto fail;
60
61 m->class = _MACHINE_CLASS_INVALID;
62 m->manager = manager;
63
64 return m;
65
66fail:
67 free(m->state_file);
68 free(m->name);
69 free(m);
70
71 return NULL;
72}
73
74void machine_free(Machine *m) {
75 assert(m);
76
0370612e
LP
77 while (m->operations)
78 machine_operation_unref(m->operations);
79
9444b1f2 80 if (m->in_gc_queue)
71fda00f 81 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2 82
9b420b3c 83 machine_release_unit(m);
9444b1f2 84
fb6becb4
LP
85 free(m->scope_job);
86
9b420b3c 87 (void) hashmap_remove(m->manager->machines, m->name);
9444b1f2 88
d3e84ddb 89 if (m->leader > 0)
9b420b3c 90 (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
d3e84ddb 91
c3350683 92 sd_bus_message_unref(m->create_message);
fb6becb4 93
9444b1f2
LP
94 free(m->name);
95 free(m->state_file);
96 free(m->service);
9444b1f2 97 free(m->root_directory);
9b5ed6fe 98 free(m->netif);
9444b1f2
LP
99 free(m);
100}
101
102int machine_save(Machine *m) {
103 _cleanup_free_ char *temp_path = NULL;
104 _cleanup_fclose_ FILE *f = NULL;
105 int r;
106
107 assert(m);
108 assert(m->state_file);
109
110 if (!m->started)
111 return 0;
112
113 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
114 if (r < 0)
dacd6cee 115 goto fail;
9444b1f2
LP
116
117 r = fopen_temporary(m->state_file, &f, &temp_path);
118 if (r < 0)
dacd6cee 119 goto fail;
9444b1f2 120
dacd6cee 121 (void) fchmod(fileno(f), 0644);
9444b1f2
LP
122
123 fprintf(f,
124 "# This is private data. Do not parse.\n"
125 "NAME=%s\n",
126 m->name);
127
ca5405bb
LP
128 if (m->unit) {
129 _cleanup_free_ char *escaped;
130
131 escaped = cescape(m->unit);
132 if (!escaped) {
133 r = -ENOMEM;
dacd6cee 134 goto fail;
ca5405bb
LP
135 }
136
137 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 */
138 }
fb6becb4
LP
139
140 if (m->scope_job)
141 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2 142
ca5405bb
LP
143 if (m->service) {
144 _cleanup_free_ char *escaped;
9444b1f2 145
ca5405bb
LP
146 escaped = cescape(m->service);
147 if (!escaped) {
148 r = -ENOMEM;
dacd6cee 149 goto fail;
ca5405bb
LP
150 }
151 fprintf(f, "SERVICE=%s\n", escaped);
152 }
153
154 if (m->root_directory) {
155 _cleanup_free_ char *escaped;
156
157 escaped = cescape(m->root_directory);
158 if (!escaped) {
159 r = -ENOMEM;
dacd6cee 160 goto fail;
ca5405bb
LP
161 }
162 fprintf(f, "ROOT=%s\n", escaped);
163 }
9444b1f2
LP
164
165 if (!sd_id128_equal(m->id, SD_ID128_NULL))
166 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
167
168 if (m->leader != 0)
90b2de37 169 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
170
171 if (m->class != _MACHINE_CLASS_INVALID)
172 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
173
174 if (dual_timestamp_is_set(&m->timestamp))
175 fprintf(f,
90b2de37
ZJS
176 "REALTIME="USEC_FMT"\n"
177 "MONOTONIC="USEC_FMT"\n",
178 m->timestamp.realtime,
179 m->timestamp.monotonic);
9444b1f2 180
9b5ed6fe
LP
181 if (m->n_netif > 0) {
182 unsigned i;
183
184 fputs("NETIF=", f);
185
186 for (i = 0; i < m->n_netif; i++) {
187 if (i != 0)
188 fputc(' ', f);
189
190 fprintf(f, "%i", m->netif[i]);
191 }
192
193 fputc('\n', f);
194 }
195
034753ac
LP
196 r = fflush_and_check(f);
197 if (r < 0)
dacd6cee 198 goto fail;
9444b1f2 199
034753ac 200 if (rename(temp_path, m->state_file) < 0) {
9444b1f2 201 r = -errno;
dacd6cee 202 goto fail;
9444b1f2
LP
203 }
204
89f7c846
LP
205 if (m->unit) {
206 char *sl;
207
208 /* Create a symlink from the unit name to the machine
209 * name, so that we can quickly find the machine for
e62d9b81 210 * each given unit. Ignore error. */
63c372cb 211 sl = strjoina("/run/systemd/machines/unit:", m->unit);
e62d9b81 212 (void) symlink(m->name, sl);
89f7c846
LP
213 }
214
dacd6cee 215 return 0;
034753ac 216
dacd6cee
LP
217fail:
218 (void) unlink(m->state_file);
219
220 if (temp_path)
221 (void) unlink(temp_path);
9444b1f2 222
dacd6cee 223 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
9444b1f2
LP
224}
225
89f7c846
LP
226static void machine_unlink(Machine *m) {
227 assert(m);
228
229 if (m->unit) {
230
231 char *sl;
232
63c372cb 233 sl = strjoina("/run/systemd/machines/unit:", m->unit);
491ac9f2 234 (void) unlink(sl);
89f7c846
LP
235 }
236
237 if (m->state_file)
491ac9f2 238 (void) unlink(m->state_file);
89f7c846
LP
239}
240
9444b1f2 241int machine_load(Machine *m) {
9b5ed6fe 242 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
9444b1f2
LP
243 int r;
244
245 assert(m);
246
247 r = parse_env_file(m->state_file, NEWLINE,
89f7c846 248 "SCOPE", &m->unit,
fb6becb4 249 "SCOPE_JOB", &m->scope_job,
9444b1f2 250 "SERVICE", &m->service,
9444b1f2
LP
251 "ROOT", &m->root_directory,
252 "ID", &id,
253 "LEADER", &leader,
254 "CLASS", &class,
255 "REALTIME", &realtime,
256 "MONOTONIC", &monotonic,
9b5ed6fe 257 "NETIF", &netif,
9444b1f2
LP
258 NULL);
259 if (r < 0) {
260 if (r == -ENOENT)
261 return 0;
262
8d3d7072 263 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
9444b1f2
LP
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
63c372cb 341 description = strjoina(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
c00a4c8f
LP
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;
fb6becb4 413 }
9444b1f2 414
fb6becb4
LP
415 free(m->scope_job);
416 m->scope_job = job;
9444b1f2 417
f14aa1f1 418 return 0;
9444b1f2
LP
419}
420
421int machine_stop(Machine *m) {
49f3fffd
LP
422 int r;
423 assert(m);
424
425 r = machine_stop_scope(m);
426
427 m->stopping = true;
428
429 machine_save(m);
430
431 return r;
432}
433
434int machine_finalize(Machine *m) {
9444b1f2
LP
435 assert(m);
436
437 if (m->started)
438 log_struct(LOG_INFO,
e2cc6eca 439 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
9444b1f2 440 "NAME=%s", m->name,
de0671ee 441 "LEADER="PID_FMT, m->leader,
e2cc6eca 442 LOG_MESSAGE("Machine %s terminated.", m->name),
9444b1f2
LP
443 NULL);
444
89f7c846 445 machine_unlink(m);
9444b1f2
LP
446 machine_add_to_gc_queue(m);
447
49f3fffd 448 if (m->started) {
9444b1f2 449 machine_send_signal(m, false);
49f3fffd
LP
450 m->started = false;
451 }
9444b1f2 452
49f3fffd 453 return 0;
9444b1f2
LP
454}
455
a658cafa 456bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
457 assert(m);
458
459 if (drop_not_started && !m->started)
c3350683 460 return false;
9444b1f2 461
c3350683
LP
462 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
463 return true;
9444b1f2 464
89f7c846 465 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 466 return true;
9444b1f2 467
c3350683 468 return false;
9444b1f2
LP
469}
470
471void machine_add_to_gc_queue(Machine *m) {
472 assert(m);
473
474 if (m->in_gc_queue)
475 return;
476
71fda00f 477 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
478 m->in_gc_queue = true;
479}
480
fb6becb4
LP
481MachineState machine_get_state(Machine *s) {
482 assert(s);
9444b1f2 483
49f3fffd
LP
484 if (s->stopping)
485 return MACHINE_CLOSING;
486
fb6becb4 487 if (s->scope_job)
49f3fffd 488 return MACHINE_OPENING;
9444b1f2 489
fb6becb4
LP
490 return MACHINE_RUNNING;
491}
9444b1f2 492
fb6becb4
LP
493int machine_kill(Machine *m, KillWho who, int signo) {
494 assert(m);
9444b1f2 495
89f7c846 496 if (!m->unit)
fb6becb4 497 return -ESRCH;
9444b1f2 498
de58a50e
LP
499 if (who == KILL_LEADER) {
500 /* If we shall simply kill the leader, do so directly */
501
502 if (kill(m->leader, signo) < 0)
503 return -errno;
9d685ca8
ED
504
505 return 0;
de58a50e
LP
506 }
507
508 /* Otherwise make PID 1 do it for us, for the entire cgroup */
509 return manager_kill_unit(m->manager, m->unit, signo, NULL);
9444b1f2
LP
510}
511
0370612e
LP
512MachineOperation *machine_operation_unref(MachineOperation *o) {
513 if (!o)
514 return NULL;
515
516 sd_event_source_unref(o->event_source);
517
518 safe_close(o->errno_fd);
519
520 if (o->pid > 1)
521 (void) kill(o->pid, SIGKILL);
522
523 sd_bus_message_unref(o->message);
524
525 if (o->machine) {
526 LIST_REMOVE(operations, o->machine->operations, o);
527 o->machine->n_operations--;
528 }
529
530 free(o);
531 return NULL;
532}
533
9b420b3c
LP
534void machine_release_unit(Machine *m) {
535 assert(m);
536
537 if (!m->unit)
538 return;
539
540 (void) hashmap_remove(m->manager->machine_units, m->unit);
541 free(m->unit);
542 m->unit = NULL;
543}
544
9444b1f2
LP
545static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
546 [MACHINE_CONTAINER] = "container",
547 [MACHINE_VM] = "vm"
548};
549
550DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
551
552static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
553 [MACHINE_OPENING] = "opening",
554 [MACHINE_RUNNING] = "running",
555 [MACHINE_CLOSING] = "closing"
556};
557
558DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
559
560static const char* const kill_who_table[_KILL_WHO_MAX] = {
561 [KILL_LEADER] = "leader",
562 [KILL_ALL] = "all"
563};
564
565DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);