]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
machined: Move image discovery logic into src/shared, so that we can make use of...
[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 "strv.h"
32 #include "fileio.h"
33 #include "special.h"
34 #include "unit-name.h"
35 #include "bus-util.h"
36 #include "bus-error.h"
37 #include "machine.h"
38 #include "machine-dbus.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 if (m->in_gc_queue)
78 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
79
80 if (m->unit) {
81 hashmap_remove(m->manager->machine_units, m->unit);
82 free(m->unit);
83 }
84
85 free(m->scope_job);
86
87 hashmap_remove(m->manager->machines, m->name);
88
89 if (m->leader > 0)
90 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 finish;
116
117 r = fopen_temporary(m->state_file, &f, &temp_path);
118 if (r < 0)
119 goto finish;
120
121 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 finish;
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 finish;
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 finish;
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 finish;
199
200 if (rename(temp_path, m->state_file) < 0) {
201 r = -errno;
202 goto finish;
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 */
211 sl = strappenda("/run/systemd/machines/unit:", m->unit);
212 symlink(m->name, sl);
213 }
214
215 finish:
216 if (r < 0) {
217 if (temp_path)
218 unlink(temp_path);
219
220 log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
221 }
222
223 return r;
224 }
225
226 static void machine_unlink(Machine *m) {
227 assert(m);
228
229 if (m->unit) {
230
231 char *sl;
232
233 sl = strappenda("/run/systemd/machines/unit:", m->unit);
234 unlink(sl);
235 }
236
237 if (m->state_file)
238 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 = strappenda(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 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 }
415 }
416
417 free(m->scope_job);
418 m->scope_job = job;
419
420 return 0;
421 }
422
423 int machine_stop(Machine *m) {
424 int r = 0, k;
425 assert(m);
426
427 if (m->started)
428 log_struct(LOG_INFO,
429 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
430 "NAME=%s", m->name,
431 "LEADER="PID_FMT, m->leader,
432 LOG_MESSAGE("Machine %s terminated.", m->name),
433 NULL);
434
435 /* Kill cgroup */
436 k = machine_stop_scope(m);
437 if (k < 0)
438 r = k;
439
440 machine_unlink(m);
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
451 bool machine_check_gc(Machine *m, bool drop_not_started) {
452 assert(m);
453
454 if (drop_not_started && !m->started)
455 return false;
456
457 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
458 return true;
459
460 if (m->unit && manager_unit_is_active(m->manager, m->unit))
461 return true;
462
463 return false;
464 }
465
466 void machine_add_to_gc_queue(Machine *m) {
467 assert(m);
468
469 if (m->in_gc_queue)
470 return;
471
472 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
473 m->in_gc_queue = true;
474 }
475
476 MachineState machine_get_state(Machine *s) {
477 assert(s);
478
479 if (s->scope_job)
480 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
481
482 return MACHINE_RUNNING;
483 }
484
485 int machine_kill(Machine *m, KillWho who, int signo) {
486 assert(m);
487
488 if (!m->unit)
489 return -ESRCH;
490
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;
496
497 return 0;
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);
502 }
503
504 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
505 [MACHINE_CONTAINER] = "container",
506 [MACHINE_VM] = "vm"
507 };
508
509 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
510
511 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
512 [MACHINE_OPENING] = "opening",
513 [MACHINE_RUNNING] = "running",
514 [MACHINE_CLOSING] = "closing"
515 };
516
517 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
518
519 static const char* const kill_who_table[_KILL_WHO_MAX] = {
520 [KILL_LEADER] = "leader",
521 [KILL_ALL] = "all"
522 };
523
524 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);