chiark / gitweb /
login: pass correct boolean type to libdbus
[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                 char *escaped = NULL;
232
233                 escaped = unit_name_escape(m->name);
234                 if (!escaped)
235                         return log_oom();
236
237                 m->scope = strjoin("machine-", escaped, ".scope", NULL);
238                 free(escaped);
239
240                 if (!m->scope)
241                         return log_oom();
242
243                 r = hashmap_put(m->manager->machine_units, m->scope, m);
244                 if (r < 0)
245                         log_warning("Failed to create mapping between unit and machine");
246         }
247
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);
254         } else {
255                 free(m->scope_job);
256                 m->scope_job = job;
257         }
258
259         return r;
260 }
261
262 int machine_start(Machine *m) {
263         int r;
264
265         assert(m);
266
267         if (m->started)
268                 return 0;
269
270         /* Create cgroup */
271         r = machine_start_scope(m);
272         if (r < 0)
273                 return r;
274
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
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
295 static int machine_stop_scope(Machine *m) {
296         DBusError error;
297         char *job;
298         int r;
299
300         assert(m);
301
302         dbus_error_init(&error);
303
304         if (!m->scope)
305                 return 0;
306
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         }
313
314         free(m->scope_job);
315         m->scope_job = job;
316
317         return r;
318 }
319
320 int 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 */
333         k = machine_stop_scope(m);
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
348 int machine_check_gc(Machine *m, bool drop_not_started) {
349         assert(m);
350
351         if (drop_not_started && !m->started)
352                 return 0;
353
354         if (m->scope_job)
355                 return 1;
356
357         if (m->scope)
358                 return manager_unit_is_active(m->manager, m->scope) != 0;
359
360         return 0;
361 }
362
363 void 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
373 MachineState machine_get_state(Machine *s) {
374         assert(s);
375
376         if (s->scope_job)
377                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
378
379         return MACHINE_RUNNING;
380 }
381
382 int machine_kill(Machine *m, KillWho who, int signo) {
383         assert(m);
384
385         if (!m->scope)
386                 return -ESRCH;
387
388         return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
389 }
390
391 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
392         [MACHINE_CONTAINER] = "container",
393         [MACHINE_VM] = "vm"
394 };
395
396 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
397
398 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
399         [MACHINE_OPENING] = "opening",
400         [MACHINE_RUNNING] = "running",
401         [MACHINE_CLOSING] = "closing"
402 };
403
404 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
405
406 static const char* const kill_who_table[_KILL_WHO_MAX] = {
407         [KILL_LEADER] = "leader",
408         [KILL_ALL] = "all"
409 };
410
411 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);