chiark / gitweb /
f33ad54db84b01e28d8e674a62d2a81f3130a052
[elogind.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(Machine, 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) {
221         _cleanup_free_ char *description = NULL;
222         DBusError error;
223         char *job;
224         int r;
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, &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                 } else {
251                         m->scope = scope;
252
253                         free(m->scope_job);
254                         m->scope_job = job;
255                 }
256         }
257
258         if (m->scope)
259                 hashmap_put(m->manager->machine_units, m->scope, m);
260
261         return r;
262 }
263
264 int machine_start(Machine *m) {
265         int r;
266
267         assert(m);
268
269         if (m->started)
270                 return 0;
271
272         /* Create cgroup */
273         r = machine_start_scope(m);
274         if (r < 0)
275                 return r;
276
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
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
297 static int machine_stop_scope(Machine *m) {
298         DBusError error;
299         char *job;
300         int r;
301
302         assert(m);
303
304         dbus_error_init(&error);
305
306         if (!m->scope)
307                 return 0;
308
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         }
315
316         free(m->scope_job);
317         m->scope_job = job;
318
319         return r;
320 }
321
322 int 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 */
335         k = machine_stop_scope(m);
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
350 int machine_check_gc(Machine *m, bool drop_not_started) {
351         assert(m);
352
353         if (drop_not_started && !m->started)
354                 return 0;
355
356         if (m->scope_job)
357                 return 1;
358
359         if (m->scope)
360                 return manager_unit_is_active(m->manager, m->scope) != 0;
361
362         return 0;
363 }
364
365 void 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
375 MachineState machine_get_state(Machine *s) {
376         assert(s);
377
378         if (s->scope_job)
379                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
380
381         return MACHINE_RUNNING;
382 }
383
384 int machine_kill(Machine *m, KillWho who, int signo) {
385         assert(m);
386
387         if (!m->scope)
388                 return -ESRCH;
389
390         return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
391 }
392
393 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
394         [MACHINE_CONTAINER] = "container",
395         [MACHINE_VM] = "vm"
396 };
397
398 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
399
400 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
401         [MACHINE_OPENING] = "opening",
402         [MACHINE_RUNNING] = "running",
403         [MACHINE_CLOSING] = "closing"
404 };
405
406 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
407
408 static const char* const kill_who_table[_KILL_WHO_MAX] = {
409         [KILL_LEADER] = "leader",
410         [KILL_ALL] = "all"
411 };
412
413 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);