]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
logind: don't misunderstand UnitRemoved signals during reloading
[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
fb6becb4
LP
26#include <systemd/sd-messages.h>
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
LP
34#include "unit-name.h"
35#include "dbus-common.h"
1ee306e1 36#include "machine.h"
9444b1f2
LP
37
38Machine* machine_new(Manager *manager, const char *name) {
39 Machine *m;
40
41 assert(manager);
42 assert(name);
43
44 m = new0(Machine, 1);
45 if (!m)
46 return NULL;
47
48 m->name = strdup(name);
49 if (!m->name)
50 goto fail;
51
52 m->state_file = strappend("/run/systemd/machines/", m->name);
53 if (!m->state_file)
54 goto fail;
55
56 if (hashmap_put(manager->machines, m->name, m) < 0)
57 goto fail;
58
59 m->class = _MACHINE_CLASS_INVALID;
60 m->manager = manager;
61
62 return m;
63
64fail:
65 free(m->state_file);
66 free(m->name);
67 free(m);
68
69 return NULL;
70}
71
72void machine_free(Machine *m) {
73 assert(m);
74
75 if (m->in_gc_queue)
76 LIST_REMOVE(Machine, gc_queue, m->manager->machine_gc_queue, m);
77
fb6becb4
LP
78 if (m->scope) {
79 hashmap_remove(m->manager->machine_units, m->scope);
80 free(m->scope);
9444b1f2
LP
81 }
82
fb6becb4
LP
83 free(m->scope_job);
84
9444b1f2
LP
85 hashmap_remove(m->manager->machines, m->name);
86
fb6becb4
LP
87 if (m->create_message)
88 dbus_message_unref(m->create_message);
89
9444b1f2
LP
90 free(m->name);
91 free(m->state_file);
92 free(m->service);
9444b1f2
LP
93 free(m->root_directory);
94 free(m);
95}
96
97int machine_save(Machine *m) {
98 _cleanup_free_ char *temp_path = NULL;
99 _cleanup_fclose_ FILE *f = NULL;
100 int r;
101
102 assert(m);
103 assert(m->state_file);
104
105 if (!m->started)
106 return 0;
107
108 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
109 if (r < 0)
110 goto finish;
111
112 r = fopen_temporary(m->state_file, &f, &temp_path);
113 if (r < 0)
114 goto finish;
115
116 fchmod(fileno(f), 0644);
117
118 fprintf(f,
119 "# This is private data. Do not parse.\n"
120 "NAME=%s\n",
121 m->name);
122
fb6becb4
LP
123 if (m->scope)
124 fprintf(f, "SCOPE=%s\n", m->scope);
125
126 if (m->scope_job)
127 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2
LP
128
129 if (m->service)
130 fprintf(f, "SERVICE=%s\n", m->service);
131
9444b1f2
LP
132 if (m->root_directory)
133 fprintf(f, "ROOT=%s\n", m->root_directory);
134
135 if (!sd_id128_equal(m->id, SD_ID128_NULL))
136 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
137
138 if (m->leader != 0)
139 fprintf(f, "LEADER=%lu\n", (unsigned long) m->leader);
140
141 if (m->class != _MACHINE_CLASS_INVALID)
142 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
143
144 if (dual_timestamp_is_set(&m->timestamp))
145 fprintf(f,
146 "REALTIME=%llu\n"
147 "MONOTONIC=%llu\n",
148 (unsigned long long) m->timestamp.realtime,
149 (unsigned long long) m->timestamp.monotonic);
150
151 fflush(f);
152
153 if (ferror(f) || rename(temp_path, m->state_file) < 0) {
154 r = -errno;
155 unlink(m->state_file);
156 unlink(temp_path);
157 }
158
159finish:
160 if (r < 0)
161 log_error("Failed to save machine data for %s: %s", m->name, strerror(-r));
162
163 return r;
164}
165
166int machine_load(Machine *m) {
167 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
168 int r;
169
170 assert(m);
171
172 r = parse_env_file(m->state_file, NEWLINE,
fb6becb4
LP
173 "SCOPE", &m->scope,
174 "SCOPE_JOB", &m->scope_job,
9444b1f2 175 "SERVICE", &m->service,
9444b1f2
LP
176 "ROOT", &m->root_directory,
177 "ID", &id,
178 "LEADER", &leader,
179 "CLASS", &class,
180 "REALTIME", &realtime,
181 "MONOTONIC", &monotonic,
182 NULL);
183 if (r < 0) {
184 if (r == -ENOENT)
185 return 0;
186
187 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
188 return r;
189 }
190
191 if (id)
192 sd_id128_from_string(id, &m->id);
193
194 if (leader)
195 parse_pid(leader, &m->leader);
196
197 if (class) {
198 MachineClass c;
199
200 c = machine_class_from_string(class);
201 if (c >= 0)
202 m->class = c;
203 }
204
205 if (realtime) {
206 unsigned long long l;
207 if (sscanf(realtime, "%llu", &l) > 0)
208 m->timestamp.realtime = l;
209 }
210
211 if (monotonic) {
212 unsigned long long l;
213 if (sscanf(monotonic, "%llu", &l) > 0)
214 m->timestamp.monotonic = l;
215 }
216
217 return r;
218}
219
fb6becb4
LP
220static int machine_start_scope(Machine *m) {
221 _cleanup_free_ char *description = NULL;
222 DBusError error;
223 char *job;
2c4c73b3 224 int r = 0;
9444b1f2
LP
225
226 assert(m);
227
fb6becb4 228 dbus_error_init(&error);
9444b1f2 229
fb6becb4 230 if (!m->scope) {
d0af76e6
LP
231 _cleanup_free_ char *escaped = NULL;
232 char *scope;
9444b1f2 233
fb6becb4 234 escaped = unit_name_escape(m->name);
9444b1f2
LP
235 if (!escaped)
236 return log_oom();
237
d0af76e6 238 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 239 if (!scope)
9444b1f2 240 return log_oom();
9444b1f2 241
d0af76e6 242 description = strappend(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 243
f526ab7e 244 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, &error, &job);
d0af76e6
LP
245 if (r < 0) {
246 log_error("Failed to start machine scope: %s", bus_error(&error, r));
247 dbus_error_free(&error);
fb6becb4 248
d0af76e6
LP
249 free(scope);
250 } else {
251 m->scope = scope;
252
253 free(m->scope_job);
254 m->scope_job = job;
255 }
9444b1f2
LP
256 }
257
d0af76e6
LP
258 if (m->scope)
259 hashmap_put(m->manager->machine_units, m->scope, m);
260
fb6becb4 261 return r;
9444b1f2
LP
262}
263
264int machine_start(Machine *m) {
265 int r;
266
267 assert(m);
268
269 if (m->started)
270 return 0;
271
fb6becb4
LP
272 /* Create cgroup */
273 r = machine_start_scope(m);
274 if (r < 0)
275 return r;
276
9444b1f2
LP
277 log_struct(LOG_INFO,
278 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
279 "NAME=%s", m->name,
280 "LEADER=%lu", (unsigned long) m->leader,
281 "MESSAGE=New machine %s.", m->name,
282 NULL);
283
9444b1f2
LP
284 if (!dual_timestamp_is_set(&m->timestamp))
285 dual_timestamp_get(&m->timestamp);
286
287 m->started = true;
288
289 /* Save new machine data */
290 machine_save(m);
291
292 machine_send_signal(m, true);
293
294 return 0;
295}
296
fb6becb4
LP
297static int machine_stop_scope(Machine *m) {
298 DBusError error;
299 char *job;
9444b1f2 300 int r;
9444b1f2
LP
301
302 assert(m);
303
fb6becb4 304 dbus_error_init(&error);
9444b1f2 305
fb6becb4
LP
306 if (!m->scope)
307 return 0;
9444b1f2 308
fb6becb4
LP
309 r = manager_stop_unit(m->manager, m->scope, &error, &job);
310 if (r < 0) {
311 log_error("Failed to stop machine scope: %s", bus_error(&error, r));
312 dbus_error_free(&error);
313 return r;
314 }
9444b1f2 315
fb6becb4
LP
316 free(m->scope_job);
317 m->scope_job = job;
9444b1f2
LP
318
319 return r;
320}
321
322int machine_stop(Machine *m) {
323 int r = 0, k;
324 assert(m);
325
326 if (m->started)
327 log_struct(LOG_INFO,
328 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
329 "NAME=%s", m->name,
330 "LEADER=%lu", (unsigned long) m->leader,
331 "MESSAGE=Machine %s terminated.", m->name,
332 NULL);
333
334 /* Kill cgroup */
fb6becb4 335 k = machine_stop_scope(m);
9444b1f2
LP
336 if (k < 0)
337 r = k;
338
339 unlink(m->state_file);
340 machine_add_to_gc_queue(m);
341
342 if (m->started)
343 machine_send_signal(m, false);
344
345 m->started = false;
346
347 return r;
348}
349
350int machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
351 assert(m);
352
353 if (drop_not_started && !m->started)
354 return 0;
355
fb6becb4
LP
356 if (m->scope_job)
357 return 1;
9444b1f2 358
fb6becb4
LP
359 if (m->scope)
360 return manager_unit_is_active(m->manager, m->scope) != 0;
9444b1f2
LP
361
362 return 0;
363}
364
365void machine_add_to_gc_queue(Machine *m) {
366 assert(m);
367
368 if (m->in_gc_queue)
369 return;
370
371 LIST_PREPEND(Machine, gc_queue, m->manager->machine_gc_queue, m);
372 m->in_gc_queue = true;
373}
374
fb6becb4
LP
375MachineState machine_get_state(Machine *s) {
376 assert(s);
9444b1f2 377
fb6becb4
LP
378 if (s->scope_job)
379 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 380
fb6becb4
LP
381 return MACHINE_RUNNING;
382}
9444b1f2 383
fb6becb4
LP
384int machine_kill(Machine *m, KillWho who, int signo) {
385 assert(m);
9444b1f2 386
fb6becb4
LP
387 if (!m->scope)
388 return -ESRCH;
9444b1f2 389
fb6becb4 390 return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
9444b1f2
LP
391}
392
393static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
394 [MACHINE_CONTAINER] = "container",
395 [MACHINE_VM] = "vm"
396};
397
398DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
399
400static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
401 [MACHINE_OPENING] = "opening",
402 [MACHINE_RUNNING] = "running",
403 [MACHINE_CLOSING] = "closing"
404};
405
406DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
407
408static const char* const kill_who_table[_KILL_WHO_MAX] = {
409 [KILL_LEADER] = "leader",
410 [KILL_ALL] = "all"
411};
412
413DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);