chiark / gitweb /
e45c443495c0650f5372d927a2c2f29b44298f47
[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->unit) {
80                 hashmap_remove(m->manager->machine_units, m->unit);
81                 free(m->unit);
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->unit)
127                 fprintf(f, "SCOPE=%s\n", m->unit); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
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="PID_FMT"\n", 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="USEC_FMT"\n"
150                         "MONOTONIC="USEC_FMT"\n",
151                         m->timestamp.realtime,
152                         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         if (m->unit) {
163                 char *sl;
164
165                 /* Create a symlink from the unit name to the machine
166                  * name, so that we can quickly find the machine for
167                  * each given unit */
168                 sl = strappenda("/run/systemd/machines/unit:", m->unit);
169                 symlink(m->name, sl);
170         }
171
172 finish:
173         if (r < 0)
174                 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
175
176         return r;
177 }
178
179 static void machine_unlink(Machine *m) {
180         assert(m);
181
182         if (m->unit) {
183
184                 char *sl;
185
186                 sl = strappenda("/run/systemd/machines/unit:", m->unit);
187                 unlink(sl);
188         }
189
190         if (m->state_file)
191                 unlink(m->state_file);
192 }
193
194 int machine_load(Machine *m) {
195         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
196         int r;
197
198         assert(m);
199
200         r = parse_env_file(m->state_file, NEWLINE,
201                            "SCOPE",     &m->unit,
202                            "SCOPE_JOB", &m->scope_job,
203                            "SERVICE",   &m->service,
204                            "ROOT",      &m->root_directory,
205                            "ID",        &id,
206                            "LEADER",    &leader,
207                            "CLASS",     &class,
208                            "REALTIME",  &realtime,
209                            "MONOTONIC", &monotonic,
210                            NULL);
211         if (r < 0) {
212                 if (r == -ENOENT)
213                         return 0;
214
215                 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
216                 return r;
217         }
218
219         if (id)
220                 sd_id128_from_string(id, &m->id);
221
222         if (leader)
223                 parse_pid(leader, &m->leader);
224
225         if (class) {
226                 MachineClass c;
227
228                 c = machine_class_from_string(class);
229                 if (c >= 0)
230                         m->class = c;
231         }
232
233         if (realtime) {
234                 unsigned long long l;
235                 if (sscanf(realtime, "%llu", &l) > 0)
236                         m->timestamp.realtime = l;
237         }
238
239         if (monotonic) {
240                 unsigned long long l;
241                 if (sscanf(monotonic, "%llu", &l) > 0)
242                         m->timestamp.monotonic = l;
243         }
244
245         return r;
246 }
247
248 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
249         int r = 0;
250
251         assert(m);
252
253         if (!m->unit) {
254                 _cleanup_free_ char *escaped = NULL;
255                 char *scope, *description, *job;
256
257                 escaped = unit_name_escape(m->name);
258                 if (!escaped)
259                         return log_oom();
260
261                 scope = strjoin("machine-", escaped, ".scope", NULL);
262                 if (!scope)
263                         return log_oom();
264
265                 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
266
267                 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
268                 if (r < 0) {
269                         log_error("Failed to start machine scope: %s", bus_error_message(error, r));
270                         free(scope);
271                         return r;
272                 } else {
273                         m->unit = scope;
274
275                         free(m->scope_job);
276                         m->scope_job = job;
277                 }
278         }
279
280         if (m->unit)
281                 hashmap_put(m->manager->machine_units, m->unit, m);
282
283         return r;
284 }
285
286 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
287         int r;
288
289         assert(m);
290
291         if (m->started)
292                 return 0;
293
294         r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
295         if (r < 0)
296                 return r;
297
298         /* Create cgroup */
299         r = machine_start_scope(m, properties, error);
300         if (r < 0)
301                 return r;
302
303         log_struct(LOG_INFO,
304                    MESSAGE_ID(SD_MESSAGE_MACHINE_START),
305                    "NAME=%s", m->name,
306                    "LEADER=%lu", (unsigned long) m->leader,
307                    "MESSAGE=New machine %s.", m->name,
308                    NULL);
309
310         if (!dual_timestamp_is_set(&m->timestamp))
311                 dual_timestamp_get(&m->timestamp);
312
313         m->started = true;
314
315         /* Save new machine data */
316         machine_save(m);
317
318         machine_send_signal(m, true);
319
320         return 0;
321 }
322
323 static int machine_stop_scope(Machine *m) {
324         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
325         char *job;
326         int r;
327
328         assert(m);
329
330         if (!m->unit)
331                 return 0;
332
333         r = manager_stop_unit(m->manager, m->unit, &error, &job);
334         if (r < 0) {
335                 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
336                 return r;
337         }
338
339         free(m->scope_job);
340         m->scope_job = job;
341
342         return r;
343 }
344
345 int machine_stop(Machine *m) {
346         int r = 0, k;
347         assert(m);
348
349         if (m->started)
350                 log_struct(LOG_INFO,
351                            MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
352                            "NAME=%s", m->name,
353                            "LEADER=%lu", (unsigned long) m->leader,
354                            "MESSAGE=Machine %s terminated.", m->name,
355                            NULL);
356
357         /* Kill cgroup */
358         k = machine_stop_scope(m);
359         if (k < 0)
360                 r = k;
361
362         machine_unlink(m);
363         machine_add_to_gc_queue(m);
364
365         if (m->started)
366                 machine_send_signal(m, false);
367
368         m->started = false;
369
370         return r;
371 }
372
373 bool machine_check_gc(Machine *m, bool drop_not_started) {
374         assert(m);
375
376         if (drop_not_started && !m->started)
377                 return false;
378
379         if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
380                 return true;
381
382         if (m->unit && manager_unit_is_active(m->manager, m->unit))
383                 return true;
384
385         return false;
386 }
387
388 void machine_add_to_gc_queue(Machine *m) {
389         assert(m);
390
391         if (m->in_gc_queue)
392                 return;
393
394         LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
395         m->in_gc_queue = true;
396 }
397
398 MachineState machine_get_state(Machine *s) {
399         assert(s);
400
401         if (s->scope_job)
402                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
403
404         return MACHINE_RUNNING;
405 }
406
407 int machine_kill(Machine *m, KillWho who, int signo) {
408         assert(m);
409
410         if (!m->unit)
411                 return -ESRCH;
412
413         return manager_kill_unit(m->manager, m->unit, who, signo, NULL);
414 }
415
416 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
417         [MACHINE_CONTAINER] = "container",
418         [MACHINE_VM] = "vm"
419 };
420
421 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
422
423 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
424         [MACHINE_OPENING] = "opening",
425         [MACHINE_RUNNING] = "running",
426         [MACHINE_CLOSING] = "closing"
427 };
428
429 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
430
431 static const char* const kill_who_table[_KILL_WHO_MAX] = {
432         [KILL_LEADER] = "leader",
433         [KILL_ALL] = "all"
434 };
435
436 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);