]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
Merge pull request #898 from poettering/machined-fix-reload
[thirdparty/systemd.git] / src / machine / machine.c
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
26 #include "sd-messages.h"
27
28 #include "util.h"
29 #include "mkdir.h"
30 #include "hashmap.h"
31 #include "fileio.h"
32 #include "special.h"
33 #include "unit-name.h"
34 #include "bus-util.h"
35 #include "bus-error.h"
36 #include "machine.h"
37 #include "machine-dbus.h"
38 #include "formats-util.h"
39
40 Machine* 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
66 fail:
67 free(m->state_file);
68 free(m->name);
69 free(m);
70
71 return NULL;
72 }
73
74 void machine_free(Machine *m) {
75 assert(m);
76
77 while (m->operations)
78 machine_operation_unref(m->operations);
79
80 if (m->in_gc_queue)
81 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
82
83 machine_release_unit(m);
84
85 free(m->scope_job);
86
87 (void) hashmap_remove(m->manager->machines, m->name);
88
89 if (m->leader > 0)
90 (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
91
92 sd_bus_message_unref(m->create_message);
93
94 free(m->name);
95 free(m->state_file);
96 free(m->service);
97 free(m->root_directory);
98 free(m->netif);
99 free(m);
100 }
101
102 int 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)
115 goto fail;
116
117 r = fopen_temporary(m->state_file, &f, &temp_path);
118 if (r < 0)
119 goto fail;
120
121 (void) fchmod(fileno(f), 0644);
122
123 fprintf(f,
124 "# This is private data. Do not parse.\n"
125 "NAME=%s\n",
126 m->name);
127
128 if (m->unit) {
129 _cleanup_free_ char *escaped;
130
131 escaped = cescape(m->unit);
132 if (!escaped) {
133 r = -ENOMEM;
134 goto fail;
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 }
139
140 if (m->scope_job)
141 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
142
143 if (m->service) {
144 _cleanup_free_ char *escaped;
145
146 escaped = cescape(m->service);
147 if (!escaped) {
148 r = -ENOMEM;
149 goto fail;
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;
160 goto fail;
161 }
162 fprintf(f, "ROOT=%s\n", escaped);
163 }
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)
169 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
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,
176 "REALTIME="USEC_FMT"\n"
177 "MONOTONIC="USEC_FMT"\n",
178 m->timestamp.realtime,
179 m->timestamp.monotonic);
180
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
196 r = fflush_and_check(f);
197 if (r < 0)
198 goto fail;
199
200 if (rename(temp_path, m->state_file) < 0) {
201 r = -errno;
202 goto fail;
203 }
204
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
210 * each given unit. Ignore error. */
211 sl = strjoina("/run/systemd/machines/unit:", m->unit);
212 (void) symlink(m->name, sl);
213 }
214
215 return 0;
216
217 fail:
218 (void) unlink(m->state_file);
219
220 if (temp_path)
221 (void) unlink(temp_path);
222
223 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
224 }
225
226 static void machine_unlink(Machine *m) {
227 assert(m);
228
229 if (m->unit) {
230
231 char *sl;
232
233 sl = strjoina("/run/systemd/machines/unit:", m->unit);
234 (void) unlink(sl);
235 }
236
237 if (m->state_file)
238 (void) unlink(m->state_file);
239 }
240
241 int machine_load(Machine *m) {
242 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
243 int r;
244
245 assert(m);
246
247 r = parse_env_file(m->state_file, NEWLINE,
248 "SCOPE", &m->unit,
249 "SCOPE_JOB", &m->scope_job,
250 "SERVICE", &m->service,
251 "ROOT", &m->root_directory,
252 "ID", &id,
253 "LEADER", &leader,
254 "CLASS", &class,
255 "REALTIME", &realtime,
256 "MONOTONIC", &monotonic,
257 "NETIF", &netif,
258 NULL);
259 if (r < 0) {
260 if (r == -ENOENT)
261 return 0;
262
263 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
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
292 if (netif) {
293 size_t l, allocated = 0, nr = 0;
294 const char *word, *state;
295 int *ni = NULL;
296
297 FOREACH_WORD(word, l, netif, state) {
298 char buf[l+1];
299 int ifi;
300
301 *(char*) (mempcpy(buf, word, l)) = 0;
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
321 return r;
322 }
323
324 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
325 int r = 0;
326
327 assert(m);
328
329 if (!m->unit) {
330 _cleanup_free_ char *escaped = NULL;
331 char *scope, *description, *job = NULL;
332
333 escaped = unit_name_escape(m->name);
334 if (!escaped)
335 return log_oom();
336
337 scope = strjoin("machine-", escaped, ".scope", NULL);
338 if (!scope)
339 return log_oom();
340
341 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
342
343 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
344 if (r < 0) {
345 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
346 free(scope);
347 return r;
348 } else {
349 m->unit = scope;
350
351 free(m->scope_job);
352 m->scope_job = job;
353 }
354 }
355
356 if (m->unit)
357 hashmap_put(m->manager->machine_units, m->unit, m);
358
359 return r;
360 }
361
362 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
363 int r;
364
365 assert(m);
366
367 if (m->started)
368 return 0;
369
370 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
371 if (r < 0)
372 return r;
373
374 /* Create cgroup */
375 r = machine_start_scope(m, properties, error);
376 if (r < 0)
377 return r;
378
379 log_struct(LOG_INFO,
380 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
381 "NAME=%s", m->name,
382 "LEADER="PID_FMT, m->leader,
383 LOG_MESSAGE("New machine %s.", m->name),
384 NULL);
385
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
399 static int machine_stop_scope(Machine *m) {
400 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
401 char *job = NULL;
402 int r;
403
404 assert(m);
405
406 if (!m->unit)
407 return 0;
408
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 }
414
415 free(m->scope_job);
416 m->scope_job = job;
417
418 return 0;
419 }
420
421 int machine_stop(Machine *m) {
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
434 int machine_finalize(Machine *m) {
435 assert(m);
436
437 if (m->started)
438 log_struct(LOG_INFO,
439 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
440 "NAME=%s", m->name,
441 "LEADER="PID_FMT, m->leader,
442 LOG_MESSAGE("Machine %s terminated.", m->name),
443 NULL);
444
445 machine_unlink(m);
446 machine_add_to_gc_queue(m);
447
448 if (m->started) {
449 machine_send_signal(m, false);
450 m->started = false;
451 }
452
453 return 0;
454 }
455
456 bool machine_check_gc(Machine *m, bool drop_not_started) {
457 assert(m);
458
459 if (drop_not_started && !m->started)
460 return false;
461
462 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
463 return true;
464
465 if (m->unit && manager_unit_is_active(m->manager, m->unit))
466 return true;
467
468 return false;
469 }
470
471 void machine_add_to_gc_queue(Machine *m) {
472 assert(m);
473
474 if (m->in_gc_queue)
475 return;
476
477 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
478 m->in_gc_queue = true;
479 }
480
481 MachineState machine_get_state(Machine *s) {
482 assert(s);
483
484 if (s->stopping)
485 return MACHINE_CLOSING;
486
487 if (s->scope_job)
488 return MACHINE_OPENING;
489
490 return MACHINE_RUNNING;
491 }
492
493 int machine_kill(Machine *m, KillWho who, int signo) {
494 assert(m);
495
496 if (!m->unit)
497 return -ESRCH;
498
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;
504
505 return 0;
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);
510 }
511
512 MachineOperation *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
534 void 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
545 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
546 [MACHINE_CONTAINER] = "container",
547 [MACHINE_VM] = "vm"
548 };
549
550 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
551
552 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
553 [MACHINE_OPENING] = "opening",
554 [MACHINE_RUNNING] = "running",
555 [MACHINE_CLOSING] = "closing"
556 };
557
558 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
559
560 static const char* const kill_who_table[_KILL_WHO_MAX] = {
561 [KILL_LEADER] = "leader",
562 [KILL_ALL] = "all"
563 };
564
565 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);