]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine.c
list: make our list macros a bit easier to use by not requring type spec on each...
[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 <systemd/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 "dbus-common.h"
36 #include "machine.h"
37
38 Machine* 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
64 fail:
65 free(m->state_file);
66 free(m->name);
67 free(m);
68
69 return NULL;
70 }
71
72 void machine_free(Machine *m) {
73 assert(m);
74
75 if (m->in_gc_queue)
76 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
77
78 if (m->scope) {
79 hashmap_remove(m->manager->machine_units, m->scope);
80 free(m->scope);
81 }
82
83 free(m->scope_job);
84
85 hashmap_remove(m->manager->machines, m->name);
86
87 if (m->create_message)
88 dbus_message_unref(m->create_message);
89
90 free(m->name);
91 free(m->state_file);
92 free(m->service);
93 free(m->root_directory);
94 free(m);
95 }
96
97 int 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
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);
128
129 if (m->service)
130 fprintf(f, "SERVICE=%s\n", m->service);
131
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
159 finish:
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
166 int 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,
173 "SCOPE", &m->scope,
174 "SCOPE_JOB", &m->scope_job,
175 "SERVICE", &m->service,
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
220 static int machine_start_scope(Machine *m, DBusMessageIter *iter) {
221 _cleanup_free_ char *description = NULL;
222 DBusError error;
223 char *job;
224 int r = 0;
225
226 assert(m);
227
228 dbus_error_init(&error);
229
230 if (!m->scope) {
231 _cleanup_free_ char *escaped = NULL;
232 char *scope;
233
234 escaped = unit_name_escape(m->name);
235 if (!escaped)
236 return log_oom();
237
238 scope = strjoin("machine-", escaped, ".scope", NULL);
239 if (!scope)
240 return log_oom();
241
242 description = strappend(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
243
244 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, iter, &error, &job);
245 if (r < 0) {
246 log_error("Failed to start machine scope: %s", bus_error(&error, r));
247 dbus_error_free(&error);
248
249 free(scope);
250 return r;
251 } else {
252 m->scope = scope;
253
254 free(m->scope_job);
255 m->scope_job = job;
256 }
257 }
258
259 if (m->scope)
260 hashmap_put(m->manager->machine_units, m->scope, m);
261
262 return r;
263 }
264
265 int machine_start(Machine *m, DBusMessageIter *iter) {
266 int r;
267
268 assert(m);
269
270 if (m->started)
271 return 0;
272
273 /* Create cgroup */
274 r = machine_start_scope(m, iter);
275 if (r < 0)
276 return r;
277
278 log_struct(LOG_INFO,
279 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
280 "NAME=%s", m->name,
281 "LEADER=%lu", (unsigned long) m->leader,
282 "MESSAGE=New machine %s.", m->name,
283 NULL);
284
285 if (!dual_timestamp_is_set(&m->timestamp))
286 dual_timestamp_get(&m->timestamp);
287
288 m->started = true;
289
290 /* Save new machine data */
291 machine_save(m);
292
293 machine_send_signal(m, true);
294
295 return 0;
296 }
297
298 static int machine_stop_scope(Machine *m) {
299 DBusError error;
300 char *job;
301 int r;
302
303 assert(m);
304
305 dbus_error_init(&error);
306
307 if (!m->scope)
308 return 0;
309
310 r = manager_stop_unit(m->manager, m->scope, &error, &job);
311 if (r < 0) {
312 log_error("Failed to stop machine scope: %s", bus_error(&error, r));
313 dbus_error_free(&error);
314 return r;
315 }
316
317 free(m->scope_job);
318 m->scope_job = job;
319
320 return r;
321 }
322
323 int machine_stop(Machine *m) {
324 int r = 0, k;
325 assert(m);
326
327 if (m->started)
328 log_struct(LOG_INFO,
329 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
330 "NAME=%s", m->name,
331 "LEADER=%lu", (unsigned long) m->leader,
332 "MESSAGE=Machine %s terminated.", m->name,
333 NULL);
334
335 /* Kill cgroup */
336 k = machine_stop_scope(m);
337 if (k < 0)
338 r = k;
339
340 unlink(m->state_file);
341 machine_add_to_gc_queue(m);
342
343 if (m->started)
344 machine_send_signal(m, false);
345
346 m->started = false;
347
348 return r;
349 }
350
351 int machine_check_gc(Machine *m, bool drop_not_started) {
352 assert(m);
353
354 if (drop_not_started && !m->started)
355 return 0;
356
357 if (m->scope_job)
358 return 1;
359
360 if (m->scope)
361 return manager_unit_is_active(m->manager, m->scope) != 0;
362
363 return 0;
364 }
365
366 void machine_add_to_gc_queue(Machine *m) {
367 assert(m);
368
369 if (m->in_gc_queue)
370 return;
371
372 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
373 m->in_gc_queue = true;
374 }
375
376 MachineState machine_get_state(Machine *s) {
377 assert(s);
378
379 if (s->scope_job)
380 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
381
382 return MACHINE_RUNNING;
383 }
384
385 int machine_kill(Machine *m, KillWho who, int signo) {
386 assert(m);
387
388 if (!m->scope)
389 return -ESRCH;
390
391 return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
392 }
393
394 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
395 [MACHINE_CONTAINER] = "container",
396 [MACHINE_VM] = "vm"
397 };
398
399 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
400
401 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
402 [MACHINE_OPENING] = "opening",
403 [MACHINE_RUNNING] = "running",
404 [MACHINE_CLOSING] = "closing"
405 };
406
407 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
408
409 static const char* const kill_who_table[_KILL_WHO_MAX] = {
410 [KILL_LEADER] = "leader",
411 [KILL_ALL] = "all"
412 };
413
414 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);