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