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