chiark / gitweb /
e4edd40dba90612342cd682e0765ffc328090614
[elogind.git] / src / login / logind-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 "cgroup-util.h"
31 #include "hashmap.h"
32 #include "strv.h"
33 #include "fileio.h"
34 #include "special.h"
35 #include "unit-name.h"
36 #include "dbus-common.h"
37 #include "logind-machine.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(Machine, 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->create_message)
89                 dbus_message_unref(m->create_message);
90
91         free(m->name);
92         free(m->state_file);
93         free(m->service);
94         free(m->root_directory);
95         free(m);
96 }
97
98 int machine_save(Machine *m) {
99         _cleanup_free_ char *temp_path = NULL;
100         _cleanup_fclose_ FILE *f = NULL;
101         int r;
102
103         assert(m);
104         assert(m->state_file);
105
106         if (!m->started)
107                 return 0;
108
109         r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
110         if (r < 0)
111                 goto finish;
112
113         r = fopen_temporary(m->state_file, &f, &temp_path);
114         if (r < 0)
115                 goto finish;
116
117         fchmod(fileno(f), 0644);
118
119         fprintf(f,
120                 "# This is private data. Do not parse.\n"
121                 "NAME=%s\n",
122                 m->name);
123
124         if (m->scope)
125                 fprintf(f, "SCOPE=%s\n", m->scope);
126
127         if (m->scope_job)
128                 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
129
130         if (m->service)
131                 fprintf(f, "SERVICE=%s\n", m->service);
132
133         if (m->root_directory)
134                 fprintf(f, "ROOT=%s\n", m->root_directory);
135
136         if (!sd_id128_equal(m->id, SD_ID128_NULL))
137                 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
138
139         if (m->leader != 0)
140                 fprintf(f, "LEADER=%lu\n", (unsigned long) m->leader);
141
142         if (m->class != _MACHINE_CLASS_INVALID)
143                 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
144
145         if (dual_timestamp_is_set(&m->timestamp))
146                 fprintf(f,
147                         "REALTIME=%llu\n"
148                         "MONOTONIC=%llu\n",
149                         (unsigned long long) m->timestamp.realtime,
150                         (unsigned long long) m->timestamp.monotonic);
151
152         fflush(f);
153
154         if (ferror(f) || rename(temp_path, m->state_file) < 0) {
155                 r = -errno;
156                 unlink(m->state_file);
157                 unlink(temp_path);
158         }
159
160 finish:
161         if (r < 0)
162                 log_error("Failed to save machine data for %s: %s", m->name, strerror(-r));
163
164         return r;
165 }
166
167 int machine_load(Machine *m) {
168         _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
169         int r;
170
171         assert(m);
172
173         r = parse_env_file(m->state_file, NEWLINE,
174                            "SCOPE",     &m->scope,
175                            "SCOPE_JOB", &m->scope_job,
176                            "SERVICE",   &m->service,
177                            "ROOT",      &m->root_directory,
178                            "ID",        &id,
179                            "LEADER",    &leader,
180                            "CLASS",     &class,
181                            "REALTIME",  &realtime,
182                            "MONOTONIC", &monotonic,
183                            NULL);
184         if (r < 0) {
185                 if (r == -ENOENT)
186                         return 0;
187
188                 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
189                 return r;
190         }
191
192         if (id)
193                 sd_id128_from_string(id, &m->id);
194
195         if (leader)
196                 parse_pid(leader, &m->leader);
197
198         if (class) {
199                 MachineClass c;
200
201                 c = machine_class_from_string(class);
202                 if (c >= 0)
203                         m->class = c;
204         }
205
206         if (realtime) {
207                 unsigned long long l;
208                 if (sscanf(realtime, "%llu", &l) > 0)
209                         m->timestamp.realtime = l;
210         }
211
212         if (monotonic) {
213                 unsigned long long l;
214                 if (sscanf(monotonic, "%llu", &l) > 0)
215                         m->timestamp.monotonic = l;
216         }
217
218         return r;
219 }
220
221 static int machine_start_scope(Machine *m) {
222         _cleanup_free_ char *description = NULL;
223         DBusError error;
224         char *job;
225         int r;
226
227         assert(m);
228
229         dbus_error_init(&error);
230
231         if (!m->scope) {
232                 _cleanup_free_ char *escaped = NULL;
233
234                 escaped = unit_name_escape(m->name);
235                 if (!escaped)
236                         return log_oom();
237
238                 m->scope = strjoin("machine.", m->name, ".scope", NULL);
239                 if (!m->scope)
240                         return log_oom();
241
242                 r = hashmap_put(m->manager->machine_units, m->scope, m);
243                 if (r < 0)
244                         log_warning("Failed to create mapping between unit and machine");
245         }
246
247         description = strappend(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
248
249         r = manager_start_scope(m->manager, m->scope, m->leader, SPECIAL_MACHINE_SLICE, description, &error, &job);
250         if (r < 0) {
251                 log_error("Failed to start machine scope: %s", bus_error(&error, r));
252                 dbus_error_free(&error);
253         }
254
255         free(m->scope_job);
256         m->scope_job = job;
257
258         return r;
259 }
260
261 int machine_start(Machine *m) {
262         int r;
263
264         assert(m);
265
266         if (m->started)
267                 return 0;
268
269         /* Create cgroup */
270         r = machine_start_scope(m);
271         if (r < 0)
272                 return r;
273
274         log_struct(LOG_INFO,
275                    MESSAGE_ID(SD_MESSAGE_MACHINE_START),
276                    "NAME=%s", m->name,
277                    "LEADER=%lu", (unsigned long) m->leader,
278                    "MESSAGE=New machine %s.", m->name,
279                    NULL);
280
281         if (!dual_timestamp_is_set(&m->timestamp))
282                 dual_timestamp_get(&m->timestamp);
283
284         m->started = true;
285
286         /* Save new machine data */
287         machine_save(m);
288
289         machine_send_signal(m, true);
290
291         return 0;
292 }
293
294 static int machine_stop_scope(Machine *m) {
295         DBusError error;
296         char *job;
297         int r;
298
299         assert(m);
300
301         dbus_error_init(&error);
302
303         if (!m->scope)
304                 return 0;
305
306         r = manager_stop_unit(m->manager, m->scope, &error, &job);
307         if (r < 0) {
308                 log_error("Failed to stop machine scope: %s", bus_error(&error, r));
309                 dbus_error_free(&error);
310                 return r;
311         }
312
313         free(m->scope_job);
314         m->scope_job = job;
315
316         return r;
317 }
318
319 int machine_stop(Machine *m) {
320         int r = 0, k;
321         assert(m);
322
323         if (m->started)
324                 log_struct(LOG_INFO,
325                            MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
326                            "NAME=%s", m->name,
327                            "LEADER=%lu", (unsigned long) m->leader,
328                            "MESSAGE=Machine %s terminated.", m->name,
329                            NULL);
330
331         /* Kill cgroup */
332         k = machine_stop_scope(m);
333         if (k < 0)
334                 r = k;
335
336         unlink(m->state_file);
337         machine_add_to_gc_queue(m);
338
339         if (m->started)
340                 machine_send_signal(m, false);
341
342         m->started = false;
343
344         return r;
345 }
346
347 int machine_check_gc(Machine *m, bool drop_not_started) {
348         assert(m);
349
350         if (drop_not_started && !m->started)
351                 return 0;
352
353         if (m->scope_job)
354                 return 1;
355
356         if (m->scope)
357                 return manager_unit_is_active(m->manager, m->scope) != 0;
358
359         return 0;
360 }
361
362 void machine_add_to_gc_queue(Machine *m) {
363         assert(m);
364
365         if (m->in_gc_queue)
366                 return;
367
368         LIST_PREPEND(Machine, gc_queue, m->manager->machine_gc_queue, m);
369         m->in_gc_queue = true;
370 }
371
372 MachineState machine_get_state(Machine *s) {
373         assert(s);
374
375         if (s->scope_job)
376                 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
377
378         return MACHINE_RUNNING;
379 }
380
381 int machine_kill(Machine *m, KillWho who, int signo) {
382         assert(m);
383
384         if (!m->scope)
385                 return -ESRCH;
386
387         return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
388 }
389
390 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
391         [MACHINE_CONTAINER] = "container",
392         [MACHINE_VM] = "vm"
393 };
394
395 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
396
397 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
398         [MACHINE_OPENING] = "opening",
399         [MACHINE_RUNNING] = "running",
400         [MACHINE_CLOSING] = "closing"
401 };
402
403 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);