]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machine.c
hwdb: add support for Alienware graphics amplifier
[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 30#include "hashmap.h"
9444b1f2
LP
31#include "fileio.h"
32#include "special.h"
fb6becb4 33#include "unit-name.h"
c3350683
LP
34#include "bus-util.h"
35#include "bus-error.h"
003dffde
LP
36#include "machine.h"
37#include "machine-dbus.h"
6482f626 38#include "formats-util.h"
9444b1f2
LP
39
40Machine* machine_new(Manager *manager, const char *name) {
41 Machine *m;
42
43 assert(manager);
44 assert(name);
45
46 m = new0(Machine, 1);
47 if (!m)
48 return NULL;
49
50 m->name = strdup(name);
51 if (!m->name)
52 goto fail;
53
54 m->state_file = strappend("/run/systemd/machines/", m->name);
55 if (!m->state_file)
56 goto fail;
57
58 if (hashmap_put(manager->machines, m->name, m) < 0)
59 goto fail;
60
61 m->class = _MACHINE_CLASS_INVALID;
62 m->manager = manager;
63
64 return m;
65
66fail:
67 free(m->state_file);
68 free(m->name);
69 free(m);
70
71 return NULL;
72}
73
74void machine_free(Machine *m) {
75 assert(m);
76
0370612e
LP
77 while (m->operations)
78 machine_operation_unref(m->operations);
79
9444b1f2 80 if (m->in_gc_queue)
71fda00f 81 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2 82
9b420b3c 83 machine_release_unit(m);
9444b1f2 84
fb6becb4
LP
85 free(m->scope_job);
86
9b420b3c 87 (void) hashmap_remove(m->manager->machines, m->name);
9444b1f2 88
d3e84ddb 89 if (m->leader > 0)
9b420b3c 90 (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
d3e84ddb 91
c3350683 92 sd_bus_message_unref(m->create_message);
fb6becb4 93
9444b1f2
LP
94 free(m->name);
95 free(m->state_file);
96 free(m->service);
9444b1f2 97 free(m->root_directory);
9b5ed6fe 98 free(m->netif);
9444b1f2
LP
99 free(m);
100}
101
102int machine_save(Machine *m) {
103 _cleanup_free_ char *temp_path = NULL;
104 _cleanup_fclose_ FILE *f = NULL;
105 int r;
106
107 assert(m);
108 assert(m->state_file);
109
110 if (!m->started)
111 return 0;
112
113 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
114 if (r < 0)
115 goto finish;
116
117 r = fopen_temporary(m->state_file, &f, &temp_path);
118 if (r < 0)
119 goto finish;
120
121 fchmod(fileno(f), 0644);
122
123 fprintf(f,
124 "# This is private data. Do not parse.\n"
125 "NAME=%s\n",
126 m->name);
127
ca5405bb
LP
128 if (m->unit) {
129 _cleanup_free_ char *escaped;
130
131 escaped = cescape(m->unit);
132 if (!escaped) {
133 r = -ENOMEM;
134 goto finish;
135 }
136
137 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 */
138 }
fb6becb4
LP
139
140 if (m->scope_job)
141 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
9444b1f2 142
ca5405bb
LP
143 if (m->service) {
144 _cleanup_free_ char *escaped;
9444b1f2 145
ca5405bb
LP
146 escaped = cescape(m->service);
147 if (!escaped) {
148 r = -ENOMEM;
149 goto finish;
150 }
151 fprintf(f, "SERVICE=%s\n", escaped);
152 }
153
154 if (m->root_directory) {
155 _cleanup_free_ char *escaped;
156
157 escaped = cescape(m->root_directory);
158 if (!escaped) {
159 r = -ENOMEM;
160 goto finish;
161 }
162 fprintf(f, "ROOT=%s\n", escaped);
163 }
9444b1f2
LP
164
165 if (!sd_id128_equal(m->id, SD_ID128_NULL))
166 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
167
168 if (m->leader != 0)
90b2de37 169 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
9444b1f2
LP
170
171 if (m->class != _MACHINE_CLASS_INVALID)
172 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
173
174 if (dual_timestamp_is_set(&m->timestamp))
175 fprintf(f,
90b2de37
ZJS
176 "REALTIME="USEC_FMT"\n"
177 "MONOTONIC="USEC_FMT"\n",
178 m->timestamp.realtime,
179 m->timestamp.monotonic);
9444b1f2 180
9b5ed6fe
LP
181 if (m->n_netif > 0) {
182 unsigned i;
183
184 fputs("NETIF=", f);
185
186 for (i = 0; i < m->n_netif; i++) {
187 if (i != 0)
188 fputc(' ', f);
189
190 fprintf(f, "%i", m->netif[i]);
191 }
192
193 fputc('\n', f);
194 }
195
034753ac
LP
196 r = fflush_and_check(f);
197 if (r < 0)
198 goto finish;
9444b1f2 199
034753ac 200 if (rename(temp_path, m->state_file) < 0) {
9444b1f2 201 r = -errno;
034753ac 202 goto finish;
9444b1f2
LP
203 }
204
17a20d64
LP
205 free(temp_path);
206 temp_path = NULL;
207
89f7c846
LP
208 if (m->unit) {
209 char *sl;
210
211 /* Create a symlink from the unit name to the machine
212 * name, so that we can quickly find the machine for
e62d9b81 213 * each given unit. Ignore error. */
63c372cb 214 sl = strjoina("/run/systemd/machines/unit:", m->unit);
e62d9b81 215 (void) symlink(m->name, sl);
89f7c846
LP
216 }
217
9444b1f2 218finish:
17a20d64
LP
219 if (temp_path)
220 unlink(temp_path);
034753ac 221
17a20d64 222 if (r < 0)
da927ba9 223 log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
9444b1f2
LP
224
225 return r;
226}
227
89f7c846
LP
228static void machine_unlink(Machine *m) {
229 assert(m);
230
231 if (m->unit) {
232
233 char *sl;
234
63c372cb 235 sl = strjoina("/run/systemd/machines/unit:", m->unit);
89f7c846
LP
236 unlink(sl);
237 }
238
239 if (m->state_file)
240 unlink(m->state_file);
241}
242
9444b1f2 243int machine_load(Machine *m) {
9b5ed6fe 244 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
9444b1f2
LP
245 int r;
246
247 assert(m);
248
249 r = parse_env_file(m->state_file, NEWLINE,
89f7c846 250 "SCOPE", &m->unit,
fb6becb4 251 "SCOPE_JOB", &m->scope_job,
9444b1f2 252 "SERVICE", &m->service,
9444b1f2
LP
253 "ROOT", &m->root_directory,
254 "ID", &id,
255 "LEADER", &leader,
256 "CLASS", &class,
257 "REALTIME", &realtime,
258 "MONOTONIC", &monotonic,
9b5ed6fe 259 "NETIF", &netif,
9444b1f2
LP
260 NULL);
261 if (r < 0) {
262 if (r == -ENOENT)
263 return 0;
264
8d3d7072 265 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
9444b1f2
LP
266 }
267
268 if (id)
269 sd_id128_from_string(id, &m->id);
270
271 if (leader)
272 parse_pid(leader, &m->leader);
273
274 if (class) {
275 MachineClass c;
276
277 c = machine_class_from_string(class);
278 if (c >= 0)
279 m->class = c;
280 }
281
282 if (realtime) {
283 unsigned long long l;
284 if (sscanf(realtime, "%llu", &l) > 0)
285 m->timestamp.realtime = l;
286 }
287
288 if (monotonic) {
289 unsigned long long l;
290 if (sscanf(monotonic, "%llu", &l) > 0)
291 m->timestamp.monotonic = l;
292 }
293
9b5ed6fe
LP
294 if (netif) {
295 size_t l, allocated = 0, nr = 0;
a2a5291b 296 const char *word, *state;
9b5ed6fe
LP
297 int *ni = NULL;
298
a2a5291b 299 FOREACH_WORD(word, l, netif, state) {
9b5ed6fe
LP
300 char buf[l+1];
301 int ifi;
302
a2a5291b 303 *(char*) (mempcpy(buf, word, l)) = 0;
9b5ed6fe
LP
304
305 if (safe_atoi(buf, &ifi) < 0)
306 continue;
307 if (ifi <= 0)
308 continue;
309
310 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
311 free(ni);
312 return log_oom();
313 }
314
315 ni[nr++] = ifi;
316 }
317
318 free(m->netif);
319 m->netif = ni;
320 m->n_netif = nr;
321 }
322
9444b1f2
LP
323 return r;
324}
325
c3350683 326static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
2c4c73b3 327 int r = 0;
9444b1f2
LP
328
329 assert(m);
330
89f7c846 331 if (!m->unit) {
d0af76e6 332 _cleanup_free_ char *escaped = NULL;
39883f62 333 char *scope, *description, *job = NULL;
9444b1f2 334
fb6becb4 335 escaped = unit_name_escape(m->name);
9444b1f2
LP
336 if (!escaped)
337 return log_oom();
338
d0af76e6 339 scope = strjoin("machine-", escaped, ".scope", NULL);
f526ab7e 340 if (!scope)
9444b1f2 341 return log_oom();
9444b1f2 342
63c372cb 343 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
9444b1f2 344
c3350683 345 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
d0af76e6 346 if (r < 0) {
c3350683 347 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
d0af76e6 348 free(scope);
f2d4f98d 349 return r;
d0af76e6 350 } else {
89f7c846 351 m->unit = scope;
d0af76e6
LP
352
353 free(m->scope_job);
354 m->scope_job = job;
355 }
9444b1f2
LP
356 }
357
89f7c846
LP
358 if (m->unit)
359 hashmap_put(m->manager->machine_units, m->unit, m);
d0af76e6 360
fb6becb4 361 return r;
9444b1f2
LP
362}
363
c3350683 364int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
9444b1f2
LP
365 int r;
366
367 assert(m);
368
369 if (m->started)
370 return 0;
371
d3e84ddb
LP
372 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
373 if (r < 0)
374 return r;
375
fb6becb4 376 /* Create cgroup */
c3350683 377 r = machine_start_scope(m, properties, error);
fb6becb4
LP
378 if (r < 0)
379 return r;
380
9444b1f2 381 log_struct(LOG_INFO,
e2cc6eca 382 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
9444b1f2 383 "NAME=%s", m->name,
de0671ee 384 "LEADER="PID_FMT, m->leader,
e2cc6eca 385 LOG_MESSAGE("New machine %s.", m->name),
9444b1f2
LP
386 NULL);
387
9444b1f2
LP
388 if (!dual_timestamp_is_set(&m->timestamp))
389 dual_timestamp_get(&m->timestamp);
390
391 m->started = true;
392
393 /* Save new machine data */
394 machine_save(m);
395
396 machine_send_signal(m, true);
397
398 return 0;
399}
400
fb6becb4 401static int machine_stop_scope(Machine *m) {
c3350683 402 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 403 char *job = NULL;
9444b1f2 404 int r;
9444b1f2
LP
405
406 assert(m);
407
89f7c846 408 if (!m->unit)
fb6becb4 409 return 0;
9444b1f2 410
c00a4c8f
LP
411 r = manager_stop_unit(m->manager, m->unit, &error, &job);
412 if (r < 0) {
413 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
414 return r;
fb6becb4 415 }
9444b1f2 416
fb6becb4
LP
417 free(m->scope_job);
418 m->scope_job = job;
9444b1f2 419
f14aa1f1 420 return 0;
9444b1f2
LP
421}
422
423int machine_stop(Machine *m) {
424 int r = 0, k;
425 assert(m);
426
427 if (m->started)
428 log_struct(LOG_INFO,
e2cc6eca 429 LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
9444b1f2 430 "NAME=%s", m->name,
de0671ee 431 "LEADER="PID_FMT, m->leader,
e2cc6eca 432 LOG_MESSAGE("Machine %s terminated.", m->name),
9444b1f2
LP
433 NULL);
434
435 /* Kill cgroup */
fb6becb4 436 k = machine_stop_scope(m);
9444b1f2
LP
437 if (k < 0)
438 r = k;
439
89f7c846 440 machine_unlink(m);
9444b1f2
LP
441 machine_add_to_gc_queue(m);
442
443 if (m->started)
444 machine_send_signal(m, false);
445
446 m->started = false;
447
448 return r;
449}
450
a658cafa 451bool machine_check_gc(Machine *m, bool drop_not_started) {
9444b1f2
LP
452 assert(m);
453
454 if (drop_not_started && !m->started)
c3350683 455 return false;
9444b1f2 456
c3350683
LP
457 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
458 return true;
9444b1f2 459
89f7c846 460 if (m->unit && manager_unit_is_active(m->manager, m->unit))
c3350683 461 return true;
9444b1f2 462
c3350683 463 return false;
9444b1f2
LP
464}
465
466void machine_add_to_gc_queue(Machine *m) {
467 assert(m);
468
469 if (m->in_gc_queue)
470 return;
471
71fda00f 472 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
9444b1f2
LP
473 m->in_gc_queue = true;
474}
475
fb6becb4
LP
476MachineState machine_get_state(Machine *s) {
477 assert(s);
9444b1f2 478
fb6becb4
LP
479 if (s->scope_job)
480 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
9444b1f2 481
fb6becb4
LP
482 return MACHINE_RUNNING;
483}
9444b1f2 484
fb6becb4
LP
485int machine_kill(Machine *m, KillWho who, int signo) {
486 assert(m);
9444b1f2 487
89f7c846 488 if (!m->unit)
fb6becb4 489 return -ESRCH;
9444b1f2 490
de58a50e
LP
491 if (who == KILL_LEADER) {
492 /* If we shall simply kill the leader, do so directly */
493
494 if (kill(m->leader, signo) < 0)
495 return -errno;
9d685ca8
ED
496
497 return 0;
de58a50e
LP
498 }
499
500 /* Otherwise make PID 1 do it for us, for the entire cgroup */
501 return manager_kill_unit(m->manager, m->unit, signo, NULL);
9444b1f2
LP
502}
503
0370612e
LP
504MachineOperation *machine_operation_unref(MachineOperation *o) {
505 if (!o)
506 return NULL;
507
508 sd_event_source_unref(o->event_source);
509
510 safe_close(o->errno_fd);
511
512 if (o->pid > 1)
513 (void) kill(o->pid, SIGKILL);
514
515 sd_bus_message_unref(o->message);
516
517 if (o->machine) {
518 LIST_REMOVE(operations, o->machine->operations, o);
519 o->machine->n_operations--;
520 }
521
522 free(o);
523 return NULL;
524}
525
9b420b3c
LP
526void machine_release_unit(Machine *m) {
527 assert(m);
528
529 if (!m->unit)
530 return;
531
532 (void) hashmap_remove(m->manager->machine_units, m->unit);
533 free(m->unit);
534 m->unit = NULL;
535}
536
9444b1f2
LP
537static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
538 [MACHINE_CONTAINER] = "container",
539 [MACHINE_VM] = "vm"
540};
541
542DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
fb6becb4
LP
543
544static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
545 [MACHINE_OPENING] = "opening",
546 [MACHINE_RUNNING] = "running",
547 [MACHINE_CLOSING] = "closing"
548};
549
550DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
1ee306e1
LP
551
552static const char* const kill_who_table[_KILL_WHO_MAX] = {
553 [KILL_LEADER] = "leader",
554 [KILL_ALL] = "all"
555};
556
557DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);