]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
login: pass correct boolean type to libdbus
[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;
9444b1f2
LP
224 int r;
225
226 assert(m);
227
fb6becb4 228 dbus_error_init(&error);
9444b1f2 229
fb6becb4 230 if (!m->scope) {
1ee306e1 231 char *escaped = NULL;
9444b1f2 232
fb6becb4 233 escaped = unit_name_escape(m->name);
9444b1f2
LP
234 if (!escaped)
235 return log_oom();
236
1ee306e1
LP
237 m->scope = strjoin("machine-", escaped, ".scope", NULL);
238 free(escaped);
239
fb6becb4 240 if (!m->scope)
9444b1f2 241 return log_oom();
9444b1f2 242
fb6becb4 243 r = hashmap_put(m->manager->machine_units, m->scope, m);
9444b1f2 244 if (r < 0)
fb6becb4 245 log_warning("Failed to create mapping between unit and machine");
9444b1f2
LP
246 }
247
fb6becb4
LP
248 description = strappend(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
249
250 r = manager_start_scope(m->manager, m->scope, m->leader, SPECIAL_MACHINE_SLICE, description, &error, &job);
251 if (r < 0) {
252 log_error("Failed to start machine scope: %s", bus_error(&error, r));
253 dbus_error_free(&error);
1ee306e1
LP
254 } else {
255 free(m->scope_job);
256 m->scope_job = job;
9444b1f2
LP
257 }
258
fb6becb4 259 return r;
9444b1f2
LP
260}
261
262int machine_start(Machine *m) {
263 int r;
264
265 assert(m);
266
267 if (m->started)
268 return 0;
269
fb6becb4
LP
270 /* Create cgroup */
271 r = machine_start_scope(m);
272 if (r < 0)
273 return r;
274
9444b1f2
LP
275 log_struct(LOG_INFO,
276 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
277 "NAME=%s", m->name,
278 "LEADER=%lu", (unsigned long) m->leader,
279 "MESSAGE=New machine %s.", m->name,
280 NULL);
281
9444b1f2
LP
282 if (!dual_timestamp_is_set(&m->timestamp))
283 dual_timestamp_get(&m->timestamp);
284
285 m->started = true;
286
287 /* Save new machine data */
288 machine_save(m);
289
290 machine_send_signal(m, true);
291
292 return 0;
293}
294
fb6becb4
LP
295static int machine_stop_scope(Machine *m) {
296 DBusError error;
297 char *job;
9444b1f2 298 int r;
9444b1f2
LP
299
300 assert(m);
301
fb6becb4 302 dbus_error_init(&error);
9444b1f2 303
fb6becb4
LP
304 if (!m->scope)
305 return 0;
9444b1f2 306
fb6becb4
LP
307 r = manager_stop_unit(m->manager, m->scope, &error, &job);
308 if (r < 0) {
309 log_error("Failed to stop machine scope: %s", bus_error(&error, r));
310 dbus_error_free(&error);
311 return r;
312 }
9444b1f2 313
fb6becb4
LP
314 free(m->scope_job);
315 m->scope_job = job;
9444b1f2
LP
316
317 return r;
318}
319
320int machine_stop(Machine *m) {
321 int r = 0, k;
322 assert(m);
323
324 if (m->started)
325 log_struct(LOG_INFO,
326 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
327 "NAME=%s", m->name,
328 "LEADER=%lu", (unsigned long) m->leader,
329 "MESSAGE=Machine %s terminated.", m->name,
330 NULL);
331
332 /* Kill cgroup */
fb6becb4 333 k = machine_stop_scope(m);
9444b1f2
LP
334 if (k < 0)
335 r = k;
336
337 unlink(m->state_file);
338 machine_add_to_gc_queue(m);
339
340 if (m->started)
341 machine_send_signal(m, false);
342
343 m->started = false;
344
345 return r;
346}
347
348int machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
349 assert(m);
350
351 if (drop_not_started && !m->started)
352 return 0;
353
fb6becb4
LP
354 if (m->scope_job)
355 return 1;
9444b1f2 356
fb6becb4
LP
357 if (m->scope)
358 return manager_unit_is_active(m->manager, m->scope) != 0;
9444b1f2
LP
359
360 return 0;
361}
362
363void machine_add_to_gc_queue(Machine *m) {
364 assert(m);
365
366 if (m->in_gc_queue)
367 return;
368
369 LIST_PREPEND(Machine, gc_queue, m->manager->machine_gc_queue, m);
370 m->in_gc_queue = true;
371}
372
fb6becb4
LP
373MachineState machine_get_state(Machine *s) {
374 assert(s);
9444b1f2 375
fb6becb4
LP
376 if (s->scope_job)
377 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 378
fb6becb4
LP
379 return MACHINE_RUNNING;
380}
9444b1f2 381
fb6becb4
LP
382int machine_kill(Machine *m, KillWho who, int signo) {
383 assert(m);
9444b1f2 384
fb6becb4
LP
385 if (!m->scope)
386 return -ESRCH;
9444b1f2 387
fb6becb4 388 return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
9444b1f2
LP
389}
390
391static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
392 [MACHINE_CONTAINER] = "container",
393 [MACHINE_VM] = "vm"
394};
395
396DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
397
398static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
399 [MACHINE_OPENING] = "opening",
400 [MACHINE_RUNNING] = "running",
401 [MACHINE_CLOSING] = "closing"
402};
403
404DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
405
406static const char* const kill_who_table[_KILL_WHO_MAX] = {
407 [KILL_LEADER] = "leader",
408 [KILL_ALL] = "all"
409};
410
411DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);