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