]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
sysusers: add minimal tool to reconstruct /etc/passwd and /etc/group from static...
[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
89f7c846
LP
79 if (m->unit) {
80 hashmap_remove(m->manager->machine_units, m->unit);
81 free(m->unit);
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
ca5405bb
LP
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 }
fb6becb4
LP
137
138 if (m->scope_job)
139 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2 140
ca5405bb
LP
141 if (m->service) {
142 _cleanup_free_ char *escaped;
9444b1f2 143
ca5405bb
LP
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 }
9444b1f2
LP
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)
90b2de37 167 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
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,
90b2de37
ZJS
174 "REALTIME="USEC_FMT"\n"
175 "MONOTONIC="USEC_FMT"\n",
176 m->timestamp.realtime,
177 m->timestamp.monotonic);
9444b1f2
LP
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
89f7c846
LP
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
9444b1f2
LP
197finish:
198 if (r < 0)
90b2de37 199 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
9444b1f2
LP
200
201 return r;
202}
203
89f7c846
LP
204static 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
9444b1f2
LP
219int 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,
89f7c846 226 "SCOPE", &m->unit,
fb6becb4 227 "SCOPE_JOB", &m->scope_job,
9444b1f2 228 "SERVICE", &m->service,
9444b1f2
LP
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
c3350683 273static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 274 int r = 0;
9444b1f2
LP
275
276 assert(m);
277
89f7c846 278 if (!m->unit) {
d0af76e6 279 _cleanup_free_ char *escaped = NULL;
39883f62 280 char *scope, *description, *job = NULL;
9444b1f2 281
fb6becb4 282 escaped = unit_name_escape(m->name);
9444b1f2
LP
283 if (!escaped)
284 return log_oom();
285
d0af76e6 286 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 287 if (!scope)
9444b1f2 288 return log_oom();
9444b1f2 289
c3350683 290 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 291
c3350683 292 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 293 if (r < 0) {
c3350683 294 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 295 free(scope);
f2d4f98d 296 return r;
d0af76e6 297 } else {
89f7c846 298 m->unit = scope;
d0af76e6
LP
299
300 free(m->scope_job);
301 m->scope_job = job;
302 }
9444b1f2
LP
303 }
304
89f7c846
LP
305 if (m->unit)
306 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 307
fb6becb4 308 return r;
9444b1f2
LP
309}
310
c3350683 311int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
312 int r;
313
314 assert(m);
315
316 if (m->started)
317 return 0;
318
d3e84ddb
LP
319 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
320 if (r < 0)
321 return r;
322
fb6becb4 323 /* Create cgroup */
c3350683 324 r = machine_start_scope(m, properties, error);
fb6becb4
LP
325 if (r < 0)
326 return r;
327
9444b1f2
LP
328 log_struct(LOG_INFO,
329 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
330 "NAME=%s", m->name,
de0671ee 331 "LEADER="PID_FMT, m->leader,
9444b1f2
LP
332 "MESSAGE=New machine %s.", m->name,
333 NULL);
334
9444b1f2
LP
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
fb6becb4 348static int machine_stop_scope(Machine *m) {
c3350683 349 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 350 char *job = NULL;
9444b1f2 351 int r;
9444b1f2
LP
352
353 assert(m);
354
89f7c846 355 if (!m->unit)
fb6becb4 356 return 0;
9444b1f2 357
89f7c846 358 r = manager_stop_unit(m->manager, m->unit, &error, &job);
fb6becb4 359 if (r < 0) {
c3350683 360 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
fb6becb4
LP
361 return r;
362 }
9444b1f2 363
fb6becb4
LP
364 free(m->scope_job);
365 m->scope_job = job;
9444b1f2
LP
366
367 return r;
368}
369
370int 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,
de0671ee 378 "LEADER="PID_FMT, m->leader,
9444b1f2
LP
379 "MESSAGE=Machine %s terminated.", m->name,
380 NULL);
381
382 /* Kill cgroup */
fb6becb4 383 k = machine_stop_scope(m);
9444b1f2
LP
384 if (k < 0)
385 r = k;
386
89f7c846 387 machine_unlink(m);
9444b1f2
LP
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
a658cafa 398bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
399 assert(m);
400
401 if (drop_not_started && !m->started)
c3350683 402 return false;
9444b1f2 403
c3350683
LP
404 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
405 return true;
9444b1f2 406
89f7c846 407 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 408 return true;
9444b1f2 409
c3350683 410 return false;
9444b1f2
LP
411}
412
413void machine_add_to_gc_queue(Machine *m) {
414 assert(m);
415
416 if (m->in_gc_queue)
417 return;
418
71fda00f 419 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
420 m->in_gc_queue = true;
421}
422
fb6becb4
LP
423MachineState machine_get_state(Machine *s) {
424 assert(s);
9444b1f2 425
fb6becb4
LP
426 if (s->scope_job)
427 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 428
fb6becb4
LP
429 return MACHINE_RUNNING;
430}
9444b1f2 431
fb6becb4
LP
432int machine_kill(Machine *m, KillWho who, int signo) {
433 assert(m);
9444b1f2 434
89f7c846 435 if (!m->unit)
fb6becb4 436 return -ESRCH;
9444b1f2 437
de58a50e
LP
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);
9444b1f2
LP
447}
448
449static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
450 [MACHINE_CONTAINER] = "container",
451 [MACHINE_VM] = "vm"
452};
453
454DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
455
456static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
457 [MACHINE_OPENING] = "opening",
458 [MACHINE_RUNNING] = "running",
459 [MACHINE_CLOSING] = "closing"
460};
461
462DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
463
464static const char* const kill_who_table[_KILL_WHO_MAX] = {
465 [KILL_LEADER] = "leader",
466 [KILL_ALL] = "all"
467};
468
469DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);