chiark / gitweb /
journal: fix against (theoretical) undefined behavior
[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         if (m->leader > 0)
89                 hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
90
91         sd_bus_message_unref(m->create_message);
92
93         free(m->name);
94         free(m->state_file);
95         free(m->service);
96         free(m->root_directory);
97         free(m);
98 }
99
100 int machine_save(Machine *m) {
101         _cleanup_free_ char *temp_path = NULL;
102         _cleanup_fclose_ FILE *f = NULL;
103         int r;
104
105         assert(m);
106         assert(m->state_file);
107
108         if (!m->started)
109                 return 0;
110
111         r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
112         if (r < 0)
113                 goto finish;
114
115         r = fopen_temporary(m->state_file, &f, &temp_path);
116         if (r < 0)
117                 goto finish;
118
119         fchmod(fileno(f), 0644);
120
121         fprintf(f,
122                 "# This is private data. Do not parse.\n"
123                 "NAME=%s\n",
124                 m->name);
125
126         if (m->scope)
127                 fprintf(f, "SCOPE=%s\n", m->scope);
128
129         if (m->scope_job)
130                 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
131
132         if (m->service)
133                 fprintf(f, "SERVICE=%s\n", m->service);
134
135         if (m->root_directory)
136                 fprintf(f, "ROOT=%s\n", m->root_directory);
137
138         if (!sd_id128_equal(m->id, SD_ID128_NULL))
139                 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
140
141         if (m->leader != 0)
142                 fprintf(f, "LEADER=%lu\n", (unsigned long) m->leader);
143
144         if (m->class != _MACHINE_CLASS_INVALID)
145                 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
146
147         if (dual_timestamp_is_set(&m->timestamp))
148                 fprintf(f,
149                         "REALTIME=%llu\n"
150                         "MONOTONIC=%llu\n",
151                         (unsigned long long) m->timestamp.realtime,
152                         (unsigned long long) m->timestamp.monotonic);
153
154         fflush(f);
155
156         if (ferror(f) || rename(temp_path, m->state_file) < 0) {
157                 r = -errno;
158                 unlink(m->state_file);
159                 unlink(temp_path);
160         }
161
162 finish:
163         if (r < 0)
164                 log_error("Failed to save machine data for %s: %s", m->name, strerror(-r));
165
166         return r;
167 }
168
169 int machine_load(Machine *m) {
170         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
171         int r;
172
173         assert(m);
174
175         r = parse_env_file(m->state_file, NEWLINE,
176                            "SCOPE",     &m->scope,
177                            "SCOPE_JOB", &m->scope_job,
178                            "SERVICE",   &m->service,
179                            "ROOT",      &m->root_directory,
180                            "ID",        &id,
181                            "LEADER",    &leader,
182                            "CLASS",     &class,
183                            "REALTIME",  &realtime,
184                            "MONOTONIC", &monotonic,
185                            NULL);
186         if (r < 0) {
187                 if (r == -ENOENT)
188                         return 0;
189
190                 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
191                 return r;
192         }
193
194         if (id)
195                 sd_id128_from_string(id, &m->id);
196
197         if (leader)
198                 parse_pid(leader, &m->leader);
199
200         if (class) {
201                 MachineClass c;
202
203                 c = machine_class_from_string(class);
204                 if (c >= 0)
205                         m->class = c;
206         }
207
208         if (realtime) {
209                 unsigned long long l;
210                 if (sscanf(realtime, "%llu", &l) > 0)
211                         m->timestamp.realtime = l;
212         }
213
214         if (monotonic) {
215                 unsigned long long l;
216                 if (sscanf(monotonic, "%llu", &l) > 0)
217                         m->timestamp.monotonic = l;
218         }
219
220         return r;
221 }
222
223 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
224         int r = 0;
225
226         assert(m);
227
228         if (!m->scope) {
229                 _cleanup_free_ char *escaped = NULL;
230                 char *scope, *description, *job;
231
232                 escaped = unit_name_escape(m->name);
233                 if (!escaped)
234                         return log_oom();
235
236                 scope = strjoin("machine-", escaped, ".scope", NULL);
237                 if (!scope)
238                         return log_oom();
239
240                 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
241
242                 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
243                 if (r < 0) {
244                         log_error("Failed to start machine scope: %s", bus_error_message(error, r));
245                         free(scope);
246                         return r;
247                 } else {
248                         m->scope = scope;
249
250                         free(m->scope_job);
251                         m->scope_job = job;
252                 }
253         }
254
255         if (m->scope)
256                 hashmap_put(m->manager->machine_units, m->scope, m);
257
258         return r;
259 }
260
261 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
262         int r;
263
264         assert(m);
265
266         if (m->started)
267                 return 0;
268
269         r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
270         if (r < 0)
271                 return r;
272
273         /* Create cgroup */
274         r = machine_start_scope(m, properties, error);
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         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
300         char *job;
301         int r;
302
303         assert(m);
304
305         if (!m->scope)
306                 return 0;
307
308         r = manager_stop_unit(m->manager, m->scope, &error, &job);
309         if (r < 0) {
310                 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
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 bool machine_check_gc(Machine *m, bool drop_not_started) {
349         assert(m);
350
351         if (drop_not_started && !m->started)
352                 return false;
353
354         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
355                 return true;
356
357         if (m->scope && manager_unit_is_active(m->manager, m->scope))
358                 return true;
359
360         return false;
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(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);