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