chiark / gitweb /
machined: forward scope properties array from client to systemd
[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, 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(Machine, 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);