]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/machine/machine.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / machine / machine.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1+ */
2/***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <errno.h>
22#include <string.h>
23#include <unistd.h>
24
25#include "sd-messages.h"
26
27#include "alloc-util.h"
28#include "bus-error.h"
29#include "bus-util.h"
30#include "escape.h"
31#include "extract-word.h"
32#include "fd-util.h"
33#include "fileio.h"
34#include "format-util.h"
35#include "hashmap.h"
36#include "machine-dbus.h"
37#include "machine.h"
38#include "mkdir.h"
39#include "parse-util.h"
40#include "process-util.h"
41#include "special.h"
42#include "stdio-util.h"
43#include "string-table.h"
44#include "terminal-util.h"
45#include "unit-name.h"
46#include "util.h"
47
48Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
49 Machine *m;
50
51 assert(manager);
52 assert(class < _MACHINE_CLASS_MAX);
53 assert(name);
54
55 /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
56 * means as much as "we don't know yet", and that we'll figure
57 * it out later when loading the state file. */
58
59 m = new0(Machine, 1);
60 if (!m)
61 return NULL;
62
63 m->name = strdup(name);
64 if (!m->name)
65 goto fail;
66
67 if (class != MACHINE_HOST) {
68 m->state_file = strappend("/run/systemd/machines/", m->name);
69 if (!m->state_file)
70 goto fail;
71 }
72
73 m->class = class;
74
75 if (hashmap_put(manager->machines, m->name, m) < 0)
76 goto fail;
77
78 m->manager = manager;
79
80 return m;
81
82fail:
83 free(m->state_file);
84 free(m->name);
85 return mfree(m);
86}
87
88void machine_free(Machine *m) {
89 assert(m);
90
91 while (m->operations)
92 operation_free(m->operations);
93
94 if (m->in_gc_queue)
95 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
96
97 machine_release_unit(m);
98
99 free(m->scope_job);
100
101 (void) hashmap_remove(m->manager->machines, m->name);
102
103 if (m->manager->host_machine == m)
104 m->manager->host_machine = NULL;
105
106 if (m->leader > 0)
107 (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
108
109 sd_bus_message_unref(m->create_message);
110
111 free(m->name);
112 free(m->state_file);
113 free(m->service);
114 free(m->root_directory);
115 free(m->netif);
116 free(m);
117}
118
119int machine_save(Machine *m) {
120 _cleanup_free_ char *temp_path = NULL;
121 _cleanup_fclose_ FILE *f = NULL;
122 int r;
123
124 assert(m);
125
126 if (!m->state_file)
127 return 0;
128
129 if (!m->started)
130 return 0;
131
132 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, false);
133 if (r < 0)
134 goto fail;
135
136 r = fopen_temporary(m->state_file, &f, &temp_path);
137 if (r < 0)
138 goto fail;
139
140 (void) fchmod(fileno(f), 0644);
141
142 fprintf(f,
143 "# This is private data. Do not parse.\n"
144 "NAME=%s\n",
145 m->name);
146
147 if (m->unit) {
148 _cleanup_free_ char *escaped;
149
150 escaped = cescape(m->unit);
151 if (!escaped) {
152 r = -ENOMEM;
153 goto fail;
154 }
155
156 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 */
157 }
158
159 if (m->scope_job)
160 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
161
162 if (m->service) {
163 _cleanup_free_ char *escaped;
164
165 escaped = cescape(m->service);
166 if (!escaped) {
167 r = -ENOMEM;
168 goto fail;
169 }
170 fprintf(f, "SERVICE=%s\n", escaped);
171 }
172
173 if (m->root_directory) {
174 _cleanup_free_ char *escaped;
175
176 escaped = cescape(m->root_directory);
177 if (!escaped) {
178 r = -ENOMEM;
179 goto fail;
180 }
181 fprintf(f, "ROOT=%s\n", escaped);
182 }
183
184 if (!sd_id128_is_null(m->id))
185 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
186
187 if (m->leader != 0)
188 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
189
190 if (m->class != _MACHINE_CLASS_INVALID)
191 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
192
193 if (dual_timestamp_is_set(&m->timestamp))
194 fprintf(f,
195 "REALTIME="USEC_FMT"\n"
196 "MONOTONIC="USEC_FMT"\n",
197 m->timestamp.realtime,
198 m->timestamp.monotonic);
199
200 if (m->n_netif > 0) {
201 unsigned i;
202
203 fputs_unlocked("NETIF=", f);
204
205 for (i = 0; i < m->n_netif; i++) {
206 if (i != 0)
207 fputc_unlocked(' ', f);
208
209 fprintf(f, "%i", m->netif[i]);
210 }
211
212 fputc_unlocked('\n', f);
213 }
214
215 r = fflush_and_check(f);
216 if (r < 0)
217 goto fail;
218
219 if (rename(temp_path, m->state_file) < 0) {
220 r = -errno;
221 goto fail;
222 }
223
224 if (m->unit) {
225 char *sl;
226
227 /* Create a symlink from the unit name to the machine
228 * name, so that we can quickly find the machine for
229 * each given unit. Ignore error. */
230 sl = strjoina("/run/systemd/machines/unit:", m->unit);
231 (void) symlink(m->name, sl);
232 }
233
234 return 0;
235
236fail:
237 (void) unlink(m->state_file);
238
239 if (temp_path)
240 (void) unlink(temp_path);
241
242 return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
243}
244
245static void machine_unlink(Machine *m) {
246 assert(m);
247
248 if (m->unit) {
249
250 char *sl;
251
252 sl = strjoina("/run/systemd/machines/unit:", m->unit);
253 (void) unlink(sl);
254 }
255
256 if (m->state_file)
257 (void) unlink(m->state_file);
258}
259
260int machine_load(Machine *m) {
261 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
262 int r;
263
264 assert(m);
265
266 if (!m->state_file)
267 return 0;
268
269 r = parse_env_file(m->state_file, NEWLINE,
270 "SCOPE", &m->unit,
271 "SCOPE_JOB", &m->scope_job,
272 "SERVICE", &m->service,
273 "ROOT", &m->root_directory,
274 "ID", &id,
275 "LEADER", &leader,
276 "CLASS", &class,
277 "REALTIME", &realtime,
278 "MONOTONIC", &monotonic,
279 "NETIF", &netif,
280 NULL);
281 if (r < 0) {
282 if (r == -ENOENT)
283 return 0;
284
285 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
286 }
287
288 if (id)
289 sd_id128_from_string(id, &m->id);
290
291 if (leader)
292 parse_pid(leader, &m->leader);
293
294 if (class) {
295 MachineClass c;
296
297 c = machine_class_from_string(class);
298 if (c >= 0)
299 m->class = c;
300 }
301
302 if (realtime)
303 timestamp_deserialize(realtime, &m->timestamp.realtime);
304 if (monotonic)
305 timestamp_deserialize(monotonic, &m->timestamp.monotonic);
306
307 if (netif) {
308 size_t allocated = 0, nr = 0;
309 const char *p;
310 int *ni = NULL;
311
312 p = netif;
313 for (;;) {
314 _cleanup_free_ char *word = NULL;
315 int ifi;
316
317 r = extract_first_word(&p, &word, NULL, 0);
318 if (r == 0)
319 break;
320 if (r == -ENOMEM)
321 return log_oom();
322 if (r < 0) {
323 log_warning_errno(r, "Failed to parse NETIF: %s", netif);
324 break;
325 }
326
327 if (parse_ifindex(word, &ifi) < 0)
328 continue;
329
330 if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
331 free(ni);
332 return log_oom();
333 }
334
335 ni[nr++] = ifi;
336 }
337
338 free(m->netif);
339 m->netif = ni;
340 m->n_netif = nr;
341 }
342
343 return r;
344}
345
346static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
347 int r = 0;
348
349 assert(m);
350 assert(m->class != MACHINE_HOST);
351
352 if (!m->unit) {
353 _cleanup_free_ char *escaped = NULL;
354 char *scope, *description, *job = NULL;
355
356 escaped = unit_name_escape(m->name);
357 if (!escaped)
358 return log_oom();
359
360 scope = strjoin("machine-", escaped, ".scope");
361 if (!scope)
362 return log_oom();
363
364 description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
365
366 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
367 if (r < 0) {
368 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
369 free(scope);
370 return r;
371 } else {
372 m->unit = scope;
373
374 free(m->scope_job);
375 m->scope_job = job;
376 }
377 }
378
379 if (m->unit)
380 hashmap_put(m->manager->machine_units, m->unit, m);
381
382 return r;
383}
384
385int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
386 int r;
387
388 assert(m);
389
390 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
391 return -EOPNOTSUPP;
392
393 if (m->started)
394 return 0;
395
396 r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
397 if (r < 0)
398 return r;
399
400 /* Create cgroup */
401 r = machine_start_scope(m, properties, error);
402 if (r < 0)
403 return r;
404
405 log_struct(LOG_INFO,
406 "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
407 "NAME=%s", m->name,
408 "LEADER="PID_FMT, m->leader,
409 LOG_MESSAGE("New machine %s.", m->name),
410 NULL);
411
412 if (!dual_timestamp_is_set(&m->timestamp))
413 dual_timestamp_get(&m->timestamp);
414
415 m->started = true;
416
417 /* Save new machine data */
418 machine_save(m);
419
420 machine_send_signal(m, true);
421
422 return 0;
423}
424
425static int machine_stop_scope(Machine *m) {
426 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
427 char *job = NULL;
428 int r;
429
430 assert(m);
431 assert(m->class != MACHINE_HOST);
432
433 if (!m->unit)
434 return 0;
435
436 r = manager_stop_unit(m->manager, m->unit, &error, &job);
437 if (r < 0) {
438 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
439 return r;
440 }
441
442 free(m->scope_job);
443 m->scope_job = job;
444
445 return 0;
446}
447
448int machine_stop(Machine *m) {
449 int r;
450 assert(m);
451
452 if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
453 return -EOPNOTSUPP;
454
455 r = machine_stop_scope(m);
456
457 m->stopping = true;
458
459 machine_save(m);
460
461 return r;
462}
463
464int machine_finalize(Machine *m) {
465 assert(m);
466
467 if (m->started)
468 log_struct(LOG_INFO,
469 "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
470 "NAME=%s", m->name,
471 "LEADER="PID_FMT, m->leader,
472 LOG_MESSAGE("Machine %s terminated.", m->name),
473 NULL);
474
475 machine_unlink(m);
476 machine_add_to_gc_queue(m);
477
478 if (m->started) {
479 machine_send_signal(m, false);
480 m->started = false;
481 }
482
483 return 0;
484}
485
486bool machine_check_gc(Machine *m, bool drop_not_started) {
487 assert(m);
488
489 if (m->class == MACHINE_HOST)
490 return true;
491
492 if (drop_not_started && !m->started)
493 return false;
494
495 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
496 return true;
497
498 if (m->unit && manager_unit_is_active(m->manager, m->unit))
499 return true;
500
501 return false;
502}
503
504void machine_add_to_gc_queue(Machine *m) {
505 assert(m);
506
507 if (m->in_gc_queue)
508 return;
509
510 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
511 m->in_gc_queue = true;
512}
513
514MachineState machine_get_state(Machine *s) {
515 assert(s);
516
517 if (s->class == MACHINE_HOST)
518 return MACHINE_RUNNING;
519
520 if (s->stopping)
521 return MACHINE_CLOSING;
522
523 if (s->scope_job)
524 return MACHINE_OPENING;
525
526 return MACHINE_RUNNING;
527}
528
529int machine_kill(Machine *m, KillWho who, int signo) {
530 assert(m);
531
532 if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
533 return -EOPNOTSUPP;
534
535 if (!m->unit)
536 return -ESRCH;
537
538 if (who == KILL_LEADER) {
539 /* If we shall simply kill the leader, do so directly */
540
541 if (kill(m->leader, signo) < 0)
542 return -errno;
543
544 return 0;
545 }
546
547 /* Otherwise, make PID 1 do it for us, for the entire cgroup */
548 return manager_kill_unit(m->manager, m->unit, signo, NULL);
549}
550
551int machine_openpt(Machine *m, int flags) {
552 assert(m);
553
554 switch (m->class) {
555
556 case MACHINE_HOST: {
557 int fd;
558
559 fd = posix_openpt(flags);
560 if (fd < 0)
561 return -errno;
562
563 if (unlockpt(fd) < 0)
564 return -errno;
565
566 return fd;
567 }
568
569 case MACHINE_CONTAINER:
570 if (m->leader <= 0)
571 return -EINVAL;
572
573 return openpt_in_namespace(m->leader, flags);
574
575 default:
576 return -EOPNOTSUPP;
577 }
578}
579
580int machine_open_terminal(Machine *m, const char *path, int mode) {
581 assert(m);
582
583 switch (m->class) {
584
585 case MACHINE_HOST:
586 return open_terminal(path, mode);
587
588 case MACHINE_CONTAINER:
589 if (m->leader <= 0)
590 return -EINVAL;
591
592 return open_terminal_in_namespace(m->leader, path, mode);
593
594 default:
595 return -EOPNOTSUPP;
596 }
597}
598
599void machine_release_unit(Machine *m) {
600 assert(m);
601
602 if (!m->unit)
603 return;
604
605 (void) hashmap_remove(m->manager->machine_units, m->unit);
606 m->unit = mfree(m->unit);
607}
608
609int machine_get_uid_shift(Machine *m, uid_t *ret) {
610 char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
611 uid_t uid_base, uid_shift, uid_range;
612 gid_t gid_base, gid_shift, gid_range;
613 _cleanup_fclose_ FILE *f = NULL;
614 int k;
615
616 assert(m);
617 assert(ret);
618
619 /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
620 * mappings. In most cases setups should be simple like this, and administrators should only care about the
621 * basic offset a container has relative to the host. This is what this function exposes.
622 *
623 * If we encounter any more complex mappings we politely refuse this with ENXIO. */
624
625 if (m->class == MACHINE_HOST) {
626 *ret = 0;
627 return 0;
628 }
629
630 if (m->class != MACHINE_CONTAINER)
631 return -EOPNOTSUPP;
632
633 xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
634 f = fopen(p, "re");
635 if (!f) {
636 if (errno == ENOENT) {
637 /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
638 *ret = 0;
639 return 0;
640 }
641
642 return -errno;
643 }
644
645 /* Read the first line. There's at least one. */
646 errno = 0;
647 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
648 if (k != 3) {
649 if (ferror(f))
650 return -errno;
651
652 return -EBADMSG;
653 }
654
655 /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
656 if (uid_base != 0)
657 return -ENXIO;
658 /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
659 if (uid_range < (uid_t) 65534U)
660 return -ENXIO;
661
662 /* If there's more than one line, then we don't support this mapping. */
663 if (fgetc(f) != EOF)
664 return -ENXIO;
665
666 fclose(f);
667
668 xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
669 f = fopen(p, "re");
670 if (!f)
671 return -errno;
672
673 /* Read the first line. There's at least one. */
674 errno = 0;
675 k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
676 if (k != 3) {
677 if (ferror(f))
678 return -errno;
679
680 return -EBADMSG;
681 }
682
683 /* If there's more than one line, then we don't support this file. */
684 if (fgetc(f) != EOF)
685 return -ENXIO;
686
687 /* If the UID and GID mapping doesn't match, we don't support this mapping. */
688 if (uid_base != (uid_t) gid_base)
689 return -ENXIO;
690 if (uid_shift != (uid_t) gid_shift)
691 return -ENXIO;
692 if (uid_range != (uid_t) gid_range)
693 return -ENXIO;
694
695 *ret = uid_shift;
696 return 0;
697}
698
699static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
700 [MACHINE_CONTAINER] = "container",
701 [MACHINE_VM] = "vm",
702 [MACHINE_HOST] = "host",
703};
704
705DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
706
707static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
708 [MACHINE_OPENING] = "opening",
709 [MACHINE_RUNNING] = "running",
710 [MACHINE_CLOSING] = "closing"
711};
712
713DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
714
715static const char* const kill_who_table[_KILL_WHO_MAX] = {
716 [KILL_LEADER] = "leader",
717 [KILL_ALL] = "all"
718};
719
720DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);