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