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