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