]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
nspawn: add --quiet switch for turning off any output noise
[thirdparty/systemd.git] / src / machine / machine.c
CommitLineData
9444b1f2
LP
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
c3350683 26#include "sd-messages.h"
fb6becb4 27
9444b1f2
LP
28#include "util.h"
29#include "mkdir.h"
9444b1f2
LP
30#include "hashmap.h"
31#include "strv.h"
32#include "fileio.h"
33#include "special.h"
fb6becb4 34#include "unit-name.h"
1ee306e1 35#include "machine.h"
c3350683
LP
36#include "bus-util.h"
37#include "bus-error.h"
9444b1f2
LP
38
39Machine* 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
65fail:
66 free(m->state_file);
67 free(m->name);
68 free(m);
69
70 return NULL;
71}
72
73void machine_free(Machine *m) {
74 assert(m);
75
76 if (m->in_gc_queue)
71fda00f 77 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2 78
fb6becb4
LP
79 if (m->scope) {
80 hashmap_remove(m->manager->machine_units, m->scope);
81 free(m->scope);
9444b1f2
LP
82 }
83
fb6becb4
LP
84 free(m->scope_job);
85
9444b1f2
LP
86 hashmap_remove(m->manager->machines, m->name);
87
d3e84ddb
LP
88 if (m->leader > 0)
89 hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
90
c3350683 91 sd_bus_message_unref(m->create_message);
fb6becb4 92
9444b1f2
LP
93 free(m->name);
94 free(m->state_file);
95 free(m->service);
9444b1f2
LP
96 free(m->root_directory);
97 free(m);
98}
99
100int 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
fb6becb4
LP
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);
9444b1f2
LP
131
132 if (m->service)
133 fprintf(f, "SERVICE=%s\n", m->service);
134
9444b1f2
LP
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
162finish:
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
169int 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,
fb6becb4
LP
176 "SCOPE", &m->scope,
177 "SCOPE_JOB", &m->scope_job,
9444b1f2 178 "SERVICE", &m->service,
9444b1f2
LP
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
c3350683 223static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 224 int r = 0;
9444b1f2
LP
225
226 assert(m);
227
fb6becb4 228 if (!m->scope) {
d0af76e6 229 _cleanup_free_ char *escaped = NULL;
c3350683 230 char *scope, *description, *job;
9444b1f2 231
fb6becb4 232 escaped = unit_name_escape(m->name);
9444b1f2
LP
233 if (!escaped)
234 return log_oom();
235
d0af76e6 236 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 237 if (!scope)
9444b1f2 238 return log_oom();
9444b1f2 239
c3350683 240 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 241
c3350683 242 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 243 if (r < 0) {
c3350683 244 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 245 free(scope);
f2d4f98d 246 return r;
d0af76e6
LP
247 } else {
248 m->scope = scope;
249
250 free(m->scope_job);
251 m->scope_job = job;
252 }
9444b1f2
LP
253 }
254
d0af76e6
LP
255 if (m->scope)
256 hashmap_put(m->manager->machine_units, m->scope, m);
257
fb6becb4 258 return r;
9444b1f2
LP
259}
260
c3350683 261int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
262 int r;
263
264 assert(m);
265
266 if (m->started)
267 return 0;
268
d3e84ddb
LP
269 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
270 if (r < 0)
271 return r;
272
fb6becb4 273 /* Create cgroup */
c3350683 274 r = machine_start_scope(m, properties, error);
fb6becb4
LP
275 if (r < 0)
276 return r;
277
9444b1f2
LP
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
9444b1f2
LP
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
fb6becb4 298static int machine_stop_scope(Machine *m) {
c3350683 299 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 300 char *job;
9444b1f2 301 int r;
9444b1f2
LP
302
303 assert(m);
304
fb6becb4
LP
305 if (!m->scope)
306 return 0;
9444b1f2 307
fb6becb4
LP
308 r = manager_stop_unit(m->manager, m->scope, &error, &job);
309 if (r < 0) {
c3350683 310 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
fb6becb4
LP
311 return r;
312 }
9444b1f2 313
fb6becb4
LP
314 free(m->scope_job);
315 m->scope_job = job;
9444b1f2
LP
316
317 return r;
318}
319
320int 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 */
fb6becb4 333 k = machine_stop_scope(m);
9444b1f2
LP
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
a658cafa 348bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
349 assert(m);
350
351 if (drop_not_started && !m->started)
c3350683 352 return false;
9444b1f2 353
c3350683
LP
354 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
355 return true;
9444b1f2 356
c3350683
LP
357 if (m->scope && manager_unit_is_active(m->manager, m->scope))
358 return true;
9444b1f2 359
c3350683 360 return false;
9444b1f2
LP
361}
362
363void machine_add_to_gc_queue(Machine *m) {
364 assert(m);
365
366 if (m->in_gc_queue)
367 return;
368
71fda00f 369 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
370 m->in_gc_queue = true;
371}
372
fb6becb4
LP
373MachineState machine_get_state(Machine *s) {
374 assert(s);
9444b1f2 375
fb6becb4
LP
376 if (s->scope_job)
377 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 378
fb6becb4
LP
379 return MACHINE_RUNNING;
380}
9444b1f2 381
fb6becb4
LP
382int machine_kill(Machine *m, KillWho who, int signo) {
383 assert(m);
9444b1f2 384
fb6becb4
LP
385 if (!m->scope)
386 return -ESRCH;
9444b1f2 387
fb6becb4 388 return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
9444b1f2
LP
389}
390
391static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
392 [MACHINE_CONTAINER] = "container",
393 [MACHINE_VM] = "vm"
394};
395
396DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
397
398static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
399 [MACHINE_OPENING] = "opening",
400 [MACHINE_RUNNING] = "running",
401 [MACHINE_CLOSING] = "closing"
402};
403
404DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
405
406static const char* const kill_who_table[_KILL_WHO_MAX] = {
407 [KILL_LEADER] = "leader",
408 [KILL_ALL] = "all"
409};
410
411DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);