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