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