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