]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-machine.c
scope: fix state string table
[thirdparty/systemd.git] / src / login / logind-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
26#include "logind-machine.h"
27#include "util.h"
28#include "mkdir.h"
29#include "cgroup-util.h"
30#include "hashmap.h"
31#include "strv.h"
32#include "fileio.h"
33#include "special.h"
34#include <systemd/sd-messages.h>
35
36Machine* machine_new(Manager *manager, const char *name) {
37 Machine *m;
38
39 assert(manager);
40 assert(name);
41
42 m = new0(Machine, 1);
43 if (!m)
44 return NULL;
45
46 m->name = strdup(name);
47 if (!m->name)
48 goto fail;
49
50 m->state_file = strappend("/run/systemd/machines/", m->name);
51 if (!m->state_file)
52 goto fail;
53
54 if (hashmap_put(manager->machines, m->name, m) < 0)
55 goto fail;
56
57 m->class = _MACHINE_CLASS_INVALID;
58 m->manager = manager;
59
60 return m;
61
62fail:
63 free(m->state_file);
64 free(m->name);
65 free(m);
66
67 return NULL;
68}
69
70void machine_free(Machine *m) {
71 assert(m);
72
73 if (m->in_gc_queue)
74 LIST_REMOVE(Machine, gc_queue, m->manager->machine_gc_queue, m);
75
76 if (m->cgroup_path) {
77 hashmap_remove(m->manager->machine_cgroups, m->cgroup_path);
78 free(m->cgroup_path);
79 }
80
81 hashmap_remove(m->manager->machines, m->name);
82
83 free(m->name);
84 free(m->state_file);
85 free(m->service);
86 free(m->slice);
87 free(m->root_directory);
88 free(m);
89}
90
91int machine_save(Machine *m) {
92 _cleanup_free_ char *temp_path = NULL;
93 _cleanup_fclose_ FILE *f = NULL;
94 int r;
95
96 assert(m);
97 assert(m->state_file);
98
99 if (!m->started)
100 return 0;
101
102 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
103 if (r < 0)
104 goto finish;
105
106 r = fopen_temporary(m->state_file, &f, &temp_path);
107 if (r < 0)
108 goto finish;
109
110 fchmod(fileno(f), 0644);
111
112 fprintf(f,
113 "# This is private data. Do not parse.\n"
114 "NAME=%s\n",
115 m->name);
116
117 if (m->cgroup_path)
118 fprintf(f, "CGROUP=%s\n", m->cgroup_path);
119
120 if (m->service)
121 fprintf(f, "SERVICE=%s\n", m->service);
122
123 if (m->slice)
124 fprintf(f, "SLICE=%s\n", m->slice);
125
126 if (m->root_directory)
127 fprintf(f, "ROOT=%s\n", m->root_directory);
128
129 if (!sd_id128_equal(m->id, SD_ID128_NULL))
130 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
131
132 if (m->leader != 0)
133 fprintf(f, "LEADER=%lu\n", (unsigned long) m->leader);
134
135 if (m->class != _MACHINE_CLASS_INVALID)
136 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
137
138 if (dual_timestamp_is_set(&m->timestamp))
139 fprintf(f,
140 "REALTIME=%llu\n"
141 "MONOTONIC=%llu\n",
142 (unsigned long long) m->timestamp.realtime,
143 (unsigned long long) m->timestamp.monotonic);
144
145 fflush(f);
146
147 if (ferror(f) || rename(temp_path, m->state_file) < 0) {
148 r = -errno;
149 unlink(m->state_file);
150 unlink(temp_path);
151 }
152
153finish:
154 if (r < 0)
155 log_error("Failed to save machine data for %s: %s", m->name, strerror(-r));
156
157 return r;
158}
159
160int machine_load(Machine *m) {
161 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
162 int r;
163
164 assert(m);
165
166 r = parse_env_file(m->state_file, NEWLINE,
167 "CGROUP", &m->cgroup_path,
168 "SERVICE", &m->service,
169 "SLICE", &m->slice,
170 "ROOT", &m->root_directory,
171 "ID", &id,
172 "LEADER", &leader,
173 "CLASS", &class,
174 "REALTIME", &realtime,
175 "MONOTONIC", &monotonic,
176 NULL);
177 if (r < 0) {
178 if (r == -ENOENT)
179 return 0;
180
181 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
182 return r;
183 }
184
185 if (id)
186 sd_id128_from_string(id, &m->id);
187
188 if (leader)
189 parse_pid(leader, &m->leader);
190
191 if (class) {
192 MachineClass c;
193
194 c = machine_class_from_string(class);
195 if (c >= 0)
196 m->class = c;
197 }
198
199 if (realtime) {
200 unsigned long long l;
201 if (sscanf(realtime, "%llu", &l) > 0)
202 m->timestamp.realtime = l;
203 }
204
205 if (monotonic) {
206 unsigned long long l;
207 if (sscanf(monotonic, "%llu", &l) > 0)
208 m->timestamp.monotonic = l;
209 }
210
211 return r;
212}
213
214static int machine_create_one_group(Machine *m, const char *controller, const char *path) {
215 int r;
216
217 assert(m);
218 assert(path);
219
220 if (m->leader > 0)
221 r = cg_create_and_attach(controller, path, m->leader);
222 else
223 r = -EINVAL;
224
225 if (r < 0) {
4ad49000 226 r = cg_create(controller, path);
9444b1f2
LP
227 if (r < 0)
228 return r;
229 }
230
231 return 0;
232}
233
234static int machine_create_cgroup(Machine *m) {
235 char **k;
236 int r;
237
238 assert(m);
239
240 if (!m->slice) {
241 m->slice = strdup(SPECIAL_MACHINE_SLICE);
242 if (!m->slice)
243 return log_oom();
244 }
245
246 if (!m->cgroup_path) {
247 _cleanup_free_ char *escaped = NULL, *slice = NULL;
248 char *name;
249
250 name = strappenda(m->name, ".machine");
251
252 escaped = cg_escape(name);
253 if (!escaped)
254 return log_oom();
255
256 r = cg_slice_to_path(m->slice, &slice);
257 if (r < 0)
258 return r;
259
260 m->cgroup_path = strjoin(m->manager->cgroup_root, "/", slice, "/", escaped, NULL);
261 if (!m->cgroup_path)
262 return log_oom();
263 }
264
265 r = machine_create_one_group(m, SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path);
266 if (r < 0) {
267 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", m->cgroup_path, strerror(-r));
268 return r;
269 }
270
271 STRV_FOREACH(k, m->manager->controllers) {
272
273 if (strv_contains(m->manager->reset_controllers, *k))
274 continue;
275
276 r = machine_create_one_group(m, *k, m->cgroup_path);
277 if (r < 0)
278 log_warning("Failed to create cgroup %s:%s: %s", *k, m->cgroup_path, strerror(-r));
279 }
280
281 if (m->leader > 0) {
282 STRV_FOREACH(k, m->manager->reset_controllers) {
283 r = cg_attach(*k, "/", m->leader);
284 if (r < 0)
285 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
286 }
287 }
288
289 r = hashmap_put(m->manager->machine_cgroups, m->cgroup_path, m);
290 if (r < 0)
291 log_warning("Failed to create mapping between cgroup and machine");
292
293 return 0;
294}
295
296int machine_start(Machine *m) {
297 int r;
298
299 assert(m);
300
301 if (m->started)
302 return 0;
303
304 log_struct(LOG_INFO,
305 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
306 "NAME=%s", m->name,
307 "LEADER=%lu", (unsigned long) m->leader,
308 "MESSAGE=New machine %s.", m->name,
309 NULL);
310
311 /* Create cgroup */
312 r = machine_create_cgroup(m);
313 if (r < 0)
314 return r;
315
316 if (!dual_timestamp_is_set(&m->timestamp))
317 dual_timestamp_get(&m->timestamp);
318
319 m->started = true;
320
321 /* Save new machine data */
322 machine_save(m);
323
324 machine_send_signal(m, true);
325
326 return 0;
327}
328
329static int machine_terminate_cgroup(Machine *m) {
330 int r;
331 char **k;
332
333 assert(m);
334
335 if (!m->cgroup_path)
336 return 0;
337
338 cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, false);
339
340 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, true);
341 if (r < 0)
342 log_error("Failed to kill machine cgroup: %s", strerror(-r));
343
344 STRV_FOREACH(k, m->manager->controllers)
345 cg_trim(*k, m->cgroup_path, true);
346
347 hashmap_remove(m->manager->machine_cgroups, m->cgroup_path);
348
349 free(m->cgroup_path);
350 m->cgroup_path = NULL;
351
352 return r;
353}
354
355int machine_stop(Machine *m) {
356 int r = 0, k;
357 assert(m);
358
359 if (m->started)
360 log_struct(LOG_INFO,
361 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
362 "NAME=%s", m->name,
363 "LEADER=%lu", (unsigned long) m->leader,
364 "MESSAGE=Machine %s terminated.", m->name,
365 NULL);
366
367 /* Kill cgroup */
368 k = machine_terminate_cgroup(m);
369 if (k < 0)
370 r = k;
371
372 unlink(m->state_file);
373 machine_add_to_gc_queue(m);
374
375 if (m->started)
376 machine_send_signal(m, false);
377
378 m->started = false;
379
380 return r;
381}
382
383int machine_check_gc(Machine *m, bool drop_not_started) {
384 int r;
385
386 assert(m);
387
388 if (drop_not_started && !m->started)
389 return 0;
390
391 if (m->cgroup_path) {
392 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, false);
393 if (r < 0)
394 return r;
395
396 if (r <= 0)
397 return 1;
398 }
399
400 return 0;
401}
402
403void machine_add_to_gc_queue(Machine *m) {
404 assert(m);
405
406 if (m->in_gc_queue)
407 return;
408
409 LIST_PREPEND(Machine, gc_queue, m->manager->machine_gc_queue, m);
410 m->in_gc_queue = true;
411}
412
413int machine_kill(Machine *m, KillWho who, int signo) {
414 _cleanup_set_free_ Set *pid_set = NULL;
415 int r = 0;
416
417 assert(m);
418
419 if (!m->cgroup_path)
420 return -ESRCH;
421
422 if (m->leader <= 0 && who == KILL_LEADER)
423 return -ESRCH;
424
425 if (m->leader > 0)
426 if (kill(m->leader, signo) < 0)
427 r = -errno;
428
429 if (who == KILL_ALL) {
430 int q;
431
432 pid_set = set_new(trivial_hash_func, trivial_compare_func);
433 if (!pid_set)
434 return log_oom();
435
436 if (m->leader > 0) {
437 q = set_put(pid_set, LONG_TO_PTR(m->leader));
438 if (q < 0)
439 r = q;
440 }
441
442 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, signo, false, true, false, pid_set);
443 if (q < 0 && (q != -EAGAIN && q != -ESRCH && q != -ENOENT))
444 r = q;
445 }
446
447 return r;
448}
449
450static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
451 [MACHINE_CONTAINER] = "container",
452 [MACHINE_VM] = "vm"
453};
454
455DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);