]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
update TODO
[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
LP
96 free(m->root_directory);
97 free(m);
98}
99
100int machine_save(Machine *m) {
101 _cleanup_free_ char *temp_path = NULL;
102 _cleanup_fclose_ FILE *f = NULL;
103 int r;
104
105 assert(m);
106 assert(m->state_file);
107
108 if (!m->started)
109 return 0;
110
111 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
112 if (r < 0)
113 goto finish;
114
115 r = fopen_temporary(m->state_file, &f, &temp_path);
116 if (r < 0)
117 goto finish;
118
119 fchmod(fileno(f), 0644);
120
121 fprintf(f,
122 "# This is private data. Do not parse.\n"
123 "NAME=%s\n",
124 m->name);
125
89f7c846
LP
126 if (m->unit)
127 fprintf(f, "SCOPE=%s\n", m->unit); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
fb6becb4
LP
128
129 if (m->scope_job)
130 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2
LP
131
132 if (m->service)
133 fprintf(f, "SERVICE=%s\n", m->service);
134
9444b1f2
LP
135 if (m->root_directory)
136 fprintf(f, "ROOT=%s\n", m->root_directory);
137
138 if (!sd_id128_equal(m->id, SD_ID128_NULL))
139 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
140
141 if (m->leader != 0)
90b2de37 142 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
143
144 if (m->class != _MACHINE_CLASS_INVALID)
145 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
146
147 if (dual_timestamp_is_set(&m->timestamp))
148 fprintf(f,
90b2de37
ZJS
149 "REALTIME="USEC_FMT"\n"
150 "MONOTONIC="USEC_FMT"\n",
151 m->timestamp.realtime,
152 m->timestamp.monotonic);
9444b1f2
LP
153
154 fflush(f);
155
156 if (ferror(f) || rename(temp_path, m->state_file) < 0) {
157 r = -errno;
158 unlink(m->state_file);
159 unlink(temp_path);
160 }
161
89f7c846
LP
162 if (m->unit) {
163 char *sl;
164
165 /* Create a symlink from the unit name to the machine
166 * name, so that we can quickly find the machine for
167 * each given unit */
168 sl = strappenda("/run/systemd/machines/unit:", m->unit);
169 symlink(m->name, sl);
170 }
171
9444b1f2
LP
172finish:
173 if (r < 0)
90b2de37 174 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
9444b1f2
LP
175
176 return r;
177}
178
89f7c846
LP
179static void machine_unlink(Machine *m) {
180 assert(m);
181
182 if (m->unit) {
183
184 char *sl;
185
186 sl = strappenda("/run/systemd/machines/unit:", m->unit);
187 unlink(sl);
188 }
189
190 if (m->state_file)
191 unlink(m->state_file);
192}
193
9444b1f2
LP
194int machine_load(Machine *m) {
195 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
196 int r;
197
198 assert(m);
199
200 r = parse_env_file(m->state_file, NEWLINE,
89f7c846 201 "SCOPE", &m->unit,
fb6becb4 202 "SCOPE_JOB", &m->scope_job,
9444b1f2 203 "SERVICE", &m->service,
9444b1f2
LP
204 "ROOT", &m->root_directory,
205 "ID", &id,
206 "LEADER", &leader,
207 "CLASS", &class,
208 "REALTIME", &realtime,
209 "MONOTONIC", &monotonic,
210 NULL);
211 if (r < 0) {
212 if (r == -ENOENT)
213 return 0;
214
215 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
216 return r;
217 }
218
219 if (id)
220 sd_id128_from_string(id, &m->id);
221
222 if (leader)
223 parse_pid(leader, &m->leader);
224
225 if (class) {
226 MachineClass c;
227
228 c = machine_class_from_string(class);
229 if (c >= 0)
230 m->class = c;
231 }
232
233 if (realtime) {
234 unsigned long long l;
235 if (sscanf(realtime, "%llu", &l) > 0)
236 m->timestamp.realtime = l;
237 }
238
239 if (monotonic) {
240 unsigned long long l;
241 if (sscanf(monotonic, "%llu", &l) > 0)
242 m->timestamp.monotonic = l;
243 }
244
245 return r;
246}
247
c3350683 248static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 249 int r = 0;
9444b1f2
LP
250
251 assert(m);
252
89f7c846 253 if (!m->unit) {
d0af76e6 254 _cleanup_free_ char *escaped = NULL;
39883f62 255 char *scope, *description, *job = NULL;
9444b1f2 256
fb6becb4 257 escaped = unit_name_escape(m->name);
9444b1f2
LP
258 if (!escaped)
259 return log_oom();
260
d0af76e6 261 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 262 if (!scope)
9444b1f2 263 return log_oom();
9444b1f2 264
c3350683 265 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 266
c3350683 267 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 268 if (r < 0) {
c3350683 269 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 270 free(scope);
f2d4f98d 271 return r;
d0af76e6 272 } else {
89f7c846 273 m->unit = scope;
d0af76e6
LP
274
275 free(m->scope_job);
276 m->scope_job = job;
277 }
9444b1f2
LP
278 }
279
89f7c846
LP
280 if (m->unit)
281 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 282
fb6becb4 283 return r;
9444b1f2
LP
284}
285
c3350683 286int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
287 int r;
288
289 assert(m);
290
291 if (m->started)
292 return 0;
293
d3e84ddb
LP
294 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
295 if (r < 0)
296 return r;
297
fb6becb4 298 /* Create cgroup */
c3350683 299 r = machine_start_scope(m, properties, error);
fb6becb4
LP
300 if (r < 0)
301 return r;
302
9444b1f2
LP
303 log_struct(LOG_INFO,
304 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
305 "NAME=%s", m->name,
306 "LEADER=%lu", (unsigned long) m->leader,
307 "MESSAGE=New machine %s.", m->name,
308 NULL);
309
9444b1f2
LP
310 if (!dual_timestamp_is_set(&m->timestamp))
311 dual_timestamp_get(&m->timestamp);
312
313 m->started = true;
314
315 /* Save new machine data */
316 machine_save(m);
317
318 machine_send_signal(m, true);
319
320 return 0;
321}
322
fb6becb4 323static int machine_stop_scope(Machine *m) {
c3350683 324 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 325 char *job = NULL;
9444b1f2 326 int r;
9444b1f2
LP
327
328 assert(m);
329
89f7c846 330 if (!m->unit)
fb6becb4 331 return 0;
9444b1f2 332
89f7c846 333 r = manager_stop_unit(m->manager, m->unit, &error, &job);
fb6becb4 334 if (r < 0) {
c3350683 335 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
fb6becb4
LP
336 return r;
337 }
9444b1f2 338
fb6becb4
LP
339 free(m->scope_job);
340 m->scope_job = job;
9444b1f2
LP
341
342 return r;
343}
344
345int machine_stop(Machine *m) {
346 int r = 0, k;
347 assert(m);
348
349 if (m->started)
350 log_struct(LOG_INFO,
351 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
352 "NAME=%s", m->name,
353 "LEADER=%lu", (unsigned long) m->leader,
354 "MESSAGE=Machine %s terminated.", m->name,
355 NULL);
356
357 /* Kill cgroup */
fb6becb4 358 k = machine_stop_scope(m);
9444b1f2
LP
359 if (k < 0)
360 r = k;
361
89f7c846 362 machine_unlink(m);
9444b1f2
LP
363 machine_add_to_gc_queue(m);
364
365 if (m->started)
366 machine_send_signal(m, false);
367
368 m->started = false;
369
370 return r;
371}
372
a658cafa 373bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
374 assert(m);
375
376 if (drop_not_started && !m->started)
c3350683 377 return false;
9444b1f2 378
c3350683
LP
379 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
380 return true;
9444b1f2 381
89f7c846 382 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 383 return true;
9444b1f2 384
c3350683 385 return false;
9444b1f2
LP
386}
387
388void machine_add_to_gc_queue(Machine *m) {
389 assert(m);
390
391 if (m->in_gc_queue)
392 return;
393
71fda00f 394 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
395 m->in_gc_queue = true;
396}
397
fb6becb4
LP
398MachineState machine_get_state(Machine *s) {
399 assert(s);
9444b1f2 400
fb6becb4
LP
401 if (s->scope_job)
402 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 403
fb6becb4
LP
404 return MACHINE_RUNNING;
405}
9444b1f2 406
fb6becb4
LP
407int machine_kill(Machine *m, KillWho who, int signo) {
408 assert(m);
9444b1f2 409
89f7c846 410 if (!m->unit)
fb6becb4 411 return -ESRCH;
9444b1f2 412
89f7c846 413 return manager_kill_unit(m->manager, m->unit, who, signo, NULL);
9444b1f2
LP
414}
415
416static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
417 [MACHINE_CONTAINER] = "container",
418 [MACHINE_VM] = "vm"
419};
420
421DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
422
423static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
424 [MACHINE_OPENING] = "opening",
425 [MACHINE_RUNNING] = "running",
426 [MACHINE_CLOSING] = "closing"
427};
428
429DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
430
431static const char* const kill_who_table[_KILL_WHO_MAX] = {
432 [KILL_LEADER] = "leader",
433 [KILL_ALL] = "all"
434};
435
436DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);