chiark / gitweb /
bus: update bus_map_all_properties()
[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 "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 "machine.h"
36 #include "bus-util.h"
37 #include "bus-error.h"
38
39 Machine* machine_new(Manager *manager, const char *name) {
40         Machine *m;
41
42         assert(manager);
43         assert(name);
44
45         m = new0(Machine, 1);
46         if (!m)
47                 return NULL;
48
49         m->name = strdup(name);
50         if (!m->name)
51                 goto fail;
52
53         m->state_file = strappend("/run/systemd/machines/", m->name);
54         if (!m->state_file)
55                 goto fail;
56
57         if (hashmap_put(manager->machines, m->name, m) < 0)
58                 goto fail;
59
60         m->class = _MACHINE_CLASS_INVALID;
61         m->manager = manager;
62
63         return m;
64
65 fail:
66         free(m->state_file);
67         free(m->name);
68         free(m);
69
70         return NULL;
71 }
72
73 void machine_free(Machine *m) {
74         assert(m);
75
76         if (m->in_gc_queue)
77                 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
78
79         if (m->scope) {
80                 hashmap_remove(m->manager->machine_units, m->scope);
81                 free(m->scope);
82         }
83
84         free(m->scope_job);
85
86         hashmap_remove(m->manager->machines, m->name);
87
88         sd_bus_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, sd_bus_message *properties, sd_bus_error *error) {
221         int r = 0;
222
223         assert(m);
224
225         if (!m->scope) {
226                 _cleanup_free_ char *escaped = NULL;
227                 char *scope, *description, *job;
228
229                 escaped = unit_name_escape(m->name);
230                 if (!escaped)
231                         return log_oom();
232
233                 scope = strjoin("machine-", escaped, ".scope", NULL);
234                 if (!scope)
235                         return log_oom();
236
237                 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
238
239                 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
240                 if (r < 0) {
241                         log_error("Failed to start machine scope: %s", bus_error_message(error, r));
242                         free(scope);
243                         return r;
244                 } else {
245                         m->scope = scope;
246
247                         free(m->scope_job);
248                         m->scope_job = job;
249                 }
250         }
251
252         if (m->scope)
253                 hashmap_put(m->manager->machine_units, m->scope, m);
254
255         return r;
256 }
257
258 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
259         int r;
260
261         assert(m);
262
263         if (m->started)
264                 return 0;
265
266         /* Create cgroup */
267         r = machine_start_scope(m, properties, error);
268         if (r < 0)
269                 return r;
270
271         log_struct(LOG_INFO,
272                    MESSAGE_ID(SD_MESSAGE_MACHINE_START),
273                    "NAME=%s", m->name,
274                    "LEADER=%lu", (unsigned long) m->leader,
275                    "MESSAGE=New machine %s.", m->name,
276                    NULL);
277
278         if (!dual_timestamp_is_set(&m->timestamp))
279                 dual_timestamp_get(&m->timestamp);
280
281         m->started = true;
282
283         /* Save new machine data */
284         machine_save(m);
285
286         machine_send_signal(m, true);
287
288         return 0;
289 }
290
291 static int machine_stop_scope(Machine *m) {
292         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
293         char *job;
294         int r;
295
296         assert(m);
297
298         if (!m->scope)
299                 return 0;
300
301         r = manager_stop_unit(m->manager, m->scope, &error, &job);
302         if (r < 0) {
303                 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
304                 return r;
305         }
306
307         free(m->scope_job);
308         m->scope_job = job;
309
310         return r;
311 }
312
313 int machine_stop(Machine *m) {
314         int r = 0, k;
315         assert(m);
316
317         if (m->started)
318                 log_struct(LOG_INFO,
319                            MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
320                            "NAME=%s", m->name,
321                            "LEADER=%lu", (unsigned long) m->leader,
322                            "MESSAGE=Machine %s terminated.", m->name,
323                            NULL);
324
325         /* Kill cgroup */
326         k = machine_stop_scope(m);
327         if (k < 0)
328                 r = k;
329
330         unlink(m->state_file);
331         machine_add_to_gc_queue(m);
332
333         if (m->started)
334                 machine_send_signal(m, false);
335
336         m->started = false;
337
338         return r;
339 }
340
341 bool machine_check_gc(Machine *m, bool drop_not_started) {
342         assert(m);
343
344         if (drop_not_started && !m->started)
345                 return false;
346
347         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
348                 return true;
349
350         if (m->scope && manager_unit_is_active(m->manager, m->scope))
351                 return true;
352
353         return false;
354 }
355
356 void machine_add_to_gc_queue(Machine *m) {
357         assert(m);
358
359         if (m->in_gc_queue)
360                 return;
361
362         LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
363         m->in_gc_queue = true;
364 }
365
366 MachineState machine_get_state(Machine *s) {
367         assert(s);
368
369         if (s->scope_job)
370                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
371
372         return MACHINE_RUNNING;
373 }
374
375 int machine_kill(Machine *m, KillWho who, int signo) {
376         assert(m);
377
378         if (!m->scope)
379                 return -ESRCH;
380
381         return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
382 }
383
384 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
385         [MACHINE_CONTAINER] = "container",
386         [MACHINE_VM] = "vm"
387 };
388
389 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
390
391 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
392         [MACHINE_OPENING] = "opening",
393         [MACHINE_RUNNING] = "running",
394         [MACHINE_CLOSING] = "closing"
395 };
396
397 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
398
399 static const char* const kill_who_table[_KILL_WHO_MAX] = {
400         [KILL_LEADER] = "leader",
401         [KILL_ALL] = "all"
402 };
403
404 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);