• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 20417900562

21 Dec 2025 07:31PM UTC coverage: 72.5% (-0.2%) from 72.701%
20417900562

push

github

DaanDeMeyer
mkosi: Use initrd as exitrd

Let's speed up image builds by avoiding building
an exitrd and instead reusing the initrd image for
the same purpose.

309142 of 426400 relevant lines covered (72.5%)

1141502.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.8
/src/machine/machine.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sched.h>
4
#include <sys/stat.h>
5
#include <unistd.h>
6

7
#include "sd-bus.h"
8
#include "sd-event.h"
9
#include "sd-messages.h"
10

11
#include "alloc-util.h"
12
#include "bus-error.h"
13
#include "bus-internal.h"
14
#include "bus-locator.h"
15
#include "bus-unit-util.h"
16
#include "env-file.h"
17
#include "errno-util.h"
18
#include "extract-word.h"
19
#include "fd-util.h"
20
#include "fileio.h"
21
#include "format-util.h"
22
#include "fs-util.h"
23
#include "hashmap.h"
24
#include "log.h"
25
#include "machine.h"
26
#include "machine-dbus.h"
27
#include "machined-resolve-hook.h"
28
#include "machined.h"
29
#include "mkdir-label.h"
30
#include "namespace-util.h"
31
#include "operation.h"
32
#include "parse-util.h"
33
#include "path-util.h"
34
#include "process-util.h"
35
#include "serialize.h"
36
#include "signal-util.h"
37
#include "socket-util.h"
38
#include "special.h"
39
#include "stdio-util.h"
40
#include "string-table.h"
41
#include "string-util.h"
42
#include "strv.h"
43
#include "terminal-util.h"
44
#include "tmpfile-util.h"
45
#include "uid-range.h"
46
#include "unit-name.h"
47
#include "user-util.h"
48

49
int machine_new(MachineClass class, const char *name, Machine **ret) {
61✔
50
        _cleanup_(machine_freep) Machine *m = NULL;
61✔
51

52
        assert(class < _MACHINE_CLASS_MAX);
61✔
53
        assert(ret);
61✔
54

55
        /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
56
         * means as much as "we don't know yet", and that we'll figure
57
         * it out later when loading the state file. */
58

59
        m = new(Machine, 1);
61✔
60
        if (!m)
61✔
61
                return -ENOMEM;
62

63
        *m = (Machine) {
61✔
64
                .class = class,
65
                .leader = PIDREF_NULL,
66
                .supervisor = PIDREF_NULL,
67
                .vsock_cid = VMADDR_CID_ANY,
68
        };
69

70
        if (name) {
61✔
71
                m->name = strdup(name);
59✔
72
                if (!m->name)
59✔
73
                        return -ENOMEM;
74
        }
75

76
        *ret = TAKE_PTR(m);
61✔
77
        return 0;
61✔
78
}
79

80
int machine_link(Manager *manager, Machine *machine) {
61✔
81
        int r;
61✔
82

83
        assert(manager);
61✔
84
        assert(machine);
61✔
85

86
        if (machine->manager)
61✔
87
                return -EEXIST;
88
        if (!machine->name)
61✔
89
                return -EINVAL;
90

91
        if (machine->class != MACHINE_HOST) {
61✔
92
                char *temp = path_join(manager->state_dir, machine->name);
53✔
93
                if (!temp)
53✔
94
                        return -ENOMEM;
61✔
95

96
                free_and_replace(machine->state_file, temp);
53✔
97
        }
98

99
        r = hashmap_put(manager->machines, machine->name, machine);
61✔
100
        if (r < 0)
61✔
101
                return r;
102

103
        machine->manager = manager;
61✔
104

105
        return 0;
61✔
106
}
107

108
Machine* machine_free(Machine *m) {
61✔
109
        if (!m)
61✔
110
                return NULL;
111

112
        while (m->operations)
61✔
113
                operation_free(m->operations);
×
114

115
        if (m->in_gc_queue) {
61✔
116
                assert(m->manager);
50✔
117
                LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
50✔
118
        }
119

120
        if (m->manager) {
61✔
121
                machine_release_unit(m);
61✔
122

123
                (void) hashmap_remove(m->manager->machines, m->name);
61✔
124

125
                if (m->manager->host_machine == m)
61✔
126
                        m->manager->host_machine = NULL;
8✔
127
        }
128

129
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
61✔
130
        if (pidref_is_set(&m->leader)) {
61✔
131
                if (m->manager)
61✔
132
                        (void) hashmap_remove_value(m->manager->machines_by_leader, &m->leader, m);
61✔
133
                pidref_done(&m->leader);
61✔
134
        }
135

136
        m->supervisor_pidfd_event_source = sd_event_source_disable_unref(m->supervisor_pidfd_event_source);
61✔
137
        pidref_done(&m->supervisor);
61✔
138

139
        sd_bus_message_unref(m->create_message);
61✔
140

141
        m->cgroup_empty_event_source = sd_event_source_disable_unref(m->cgroup_empty_event_source);
61✔
142

143
        free(m->name);
61✔
144

145
        free(m->state_file);
61✔
146
        free(m->service);
61✔
147
        free(m->root_directory);
61✔
148

149
        free(m->unit);
61✔
150
        free(m->subgroup);
61✔
151
        free(m->scope_job);
61✔
152
        free(m->cgroup);
61✔
153

154
        free(m->netif);
61✔
155
        free(m->ssh_address);
61✔
156
        free(m->ssh_private_key_path);
61✔
157

158
        return mfree(m);
61✔
159
}
160

161
int machine_save(Machine *m) {
99✔
162
        int r;
99✔
163

164
        assert(m);
99✔
165

166
        if (!m->state_file)
99✔
167
                return 0;
99✔
168

169
        if (!m->started)
99✔
170
                return 0;
171

172
        _cleanup_(unlink_and_freep) char *sl = NULL; /* auto-unlink! */
98✔
173
        if (m->unit && !m->subgroup) {
98✔
174
                sl = strjoin(m->manager->state_dir, "/unit:", m->unit);
55✔
175
                if (!sl)
55✔
176
                        return log_oom();
×
177
        }
178

179
        r = mkdir_safe_label(m->manager->state_dir, 0755, 0, 0, MKDIR_WARN_MODE);
98✔
180
        if (r < 0)
98✔
181
                return log_error_errno(r, "Failed to create '%s': %m", m->manager->state_dir);
7✔
182

183
        _cleanup_(unlink_and_freep) char *temp_path = NULL;
×
184
        _cleanup_fclose_ FILE *f = NULL;
91✔
185
        r = fopen_tmpfile_linkable(m->state_file, O_WRONLY|O_CLOEXEC, &temp_path, &f);
91✔
186
        if (r < 0)
91✔
187
                return log_error_errno(r, "Failed to create state file '%s': %m", m->state_file);
×
188

189
        if (fchmod(fileno(f), 0644) < 0)
91✔
190
                return log_error_errno(errno, "Failed to set access mode for state file '%s' to 0644: %m", m->state_file);
×
191

192
        fprintf(f,
91✔
193
                "# This is private data. Do not parse.\n"
194
                "NAME=%s\n"
195
                "UID=" UID_FMT "\n",
196
                m->name,
197
                m->uid);
198

199
        /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
200
        env_file_fputs_assignment(f, "SCOPE=", m->unit);
91✔
201
        env_file_fputs_assignment(f, "SCOPE_JOB=", m->scope_job);
91✔
202

203
        env_file_fputs_assignment(f, "SERVICE=", m->service);
91✔
204
        env_file_fputs_assignment(f, "ROOT=", m->root_directory);
91✔
205

206
        if (!sd_id128_is_null(m->id))
91✔
207
                fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
89✔
208

209
        if (pidref_is_set(&m->leader)) {
182✔
210
                fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
91✔
211
                (void) pidref_acquire_pidfd_id(&m->leader);
91✔
212
                if (m->leader.fd_id != 0)
91✔
213
                        fprintf(f, "LEADER_PIDFDID=%" PRIu64 "\n", m->leader.fd_id);
91✔
214
        }
215

216
        if (pidref_is_set(&m->supervisor)) {
217
                fprintf(f, "SUPERVISOR=" PID_FMT "\n", m->supervisor.pid);
89✔
218
                (void) pidref_acquire_pidfd_id(&m->supervisor);
89✔
219
                if (m->supervisor.fd_id != 0)
89✔
220
                        fprintf(f, "SUPERVISOR_PIDFDID=%" PRIu64 "\n", m->supervisor.fd_id);
89✔
221
        }
222

223
        if (m->class != _MACHINE_CLASS_INVALID)
91✔
224
                fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
91✔
225

226
        if (dual_timestamp_is_set(&m->timestamp))
91✔
227
                fprintf(f,
91✔
228
                        "REALTIME="USEC_FMT"\n"
229
                        "MONOTONIC="USEC_FMT"\n",
230
                        m->timestamp.realtime,
231
                        m->timestamp.monotonic);
232

233
        if (m->n_netif > 0) {
91✔
234
                fputs("NETIF=\"", f);
35✔
235
                FOREACH_ARRAY(ifi, m->netif, m->n_netif) {
70✔
236
                        if (*ifi != 0)
35✔
237
                                fputc(' ', f);
35✔
238
                        fprintf(f, "%i", *ifi);
35✔
239
                }
240
                fputs("\"\n", f);
35✔
241
        }
242

243
        if (m->vsock_cid != 0)
91✔
244
                fprintf(f, "VSOCK_CID=%u\n", m->vsock_cid);
2✔
245

246
        env_file_fputs_assignment(f, "SSH_ADDRESS=", m->ssh_address);
91✔
247
        env_file_fputs_assignment(f, "SSH_PRIVATE_KEY_PATH=", m->ssh_private_key_path);
91✔
248

249
        r = flink_tmpfile(f, temp_path, m->state_file, LINK_TMPFILE_REPLACE);
91✔
250
        if (r < 0)
91✔
251
                return log_error_errno(r, "Failed to move '%s' into place: %m", m->state_file);
×
252

253
        temp_path = mfree(temp_path); /* disarm auto-destroy: temporary file does not exist anymore */
91✔
254

255
        if (sl) {
91✔
256
                /* Create a symlink from the unit name to the machine name, so that we can quickly find the machine
257
                 * for each given unit. Ignore error. */
258
                (void) symlink(m->name, sl);
55✔
259

260
                /* disarm auto-removal */
261
                sl = mfree(sl);
55✔
262
        }
263

264
        return 0;
265
}
266

267
static void machine_unlink(Machine *m) {
61✔
268
        assert(m);
61✔
269

270
        if (m->unit && !m->subgroup) {
61✔
271
                const char *sl = strjoina(m->manager->state_dir, "/unit:", m->unit);
231✔
272
                (void) unlink(sl);
33✔
273
        }
274

275
        if (m->state_file)
61✔
276
                (void) unlink(m->state_file);
61✔
277
}
61✔
278

279
static void parse_pid_and_pidfdid(
×
280
                PidRef *pidref,
281
                const char *pid,
282
                const char *pidfdid,
283
                const char *name) {
284

285
        int r;
×
286

287
        assert(pidref);
×
288
        assert(name);
×
289

290
        pidref_done(pidref);
×
291

292
        if (!pid)
×
293
                return;
×
294
        r = pidref_set_pidstr(pidref, pid);
×
295
        if (r < 0)
×
296
                return (void) log_debug_errno(r, "Failed to set %s PID to '%s', ignoring: %m", name, pid);
×
297

298
        if (!pidfdid)
×
299
                return;
300
        uint64_t fd_id;
×
301
        r = safe_atou64(pidfdid, &fd_id);
×
302
        if (r < 0)
×
303
                return (void) log_warning_errno(r, "Failed to parse %s pidfd ID, ignoring: %s", name, pidfdid);
×
304
        (void) pidref_acquire_pidfd_id(pidref);
×
305
        if (fd_id != pidref->fd_id) {
×
306
                log_debug("PID of %s got recycled, ignoring.", name);
×
307
                pidref_done(pidref);
×
308
        }
309
}
310

311
int machine_load(Machine *m) {
×
312
        _cleanup_free_ char *name = NULL, *realtime = NULL, *monotonic = NULL, *id = NULL,
×
313
                *leader = NULL, *leader_pidfdid = NULL, *supervisor = NULL, *supervisor_pidfdid = NULL,
×
314
                *class = NULL, *netif = NULL, *vsock_cid = NULL, *uid = NULL;
×
315
        int r;
×
316

317
        assert(m);
×
318

319
        if (!m->state_file)
×
320
                return 0;
321

322
        r = parse_env_file(NULL, m->state_file,
×
323
                           "NAME",                 &name,
324
                           "SCOPE",                &m->unit,
325
                           "SUBGROUP",             &m->subgroup,
326
                           "SCOPE_JOB",            &m->scope_job,
327
                           "SERVICE",              &m->service,
328
                           "ROOT",                 &m->root_directory,
329
                           "ID",                   &id,
330
                           "LEADER",               &leader,
331
                           "LEADER_PIDFDID",       &leader_pidfdid,
332
                           "SUPERVISOR",           &supervisor,
333
                           "SUPERVISOR_PIDFDID",   &supervisor_pidfdid,
334
                           "CLASS",                &class,
335
                           "REALTIME",             &realtime,
336
                           "MONOTONIC",            &monotonic,
337
                           "NETIF",                &netif,
338
                           "VSOCK_CID",            &vsock_cid,
339
                           "SSH_ADDRESS",          &m->ssh_address,
340
                           "SSH_PRIVATE_KEY_PATH", &m->ssh_private_key_path,
341
                           "UID",                  &uid);
342
        if (r == -ENOENT)
×
343
                return 0;
344
        if (r < 0)
×
345
                return log_error_errno(r, "Failed to read %s: %m", m->state_file);
×
346

347
        if (!streq_ptr(name, m->name))
×
348
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "State file '%s' for machine '%s' reports a different name '%s', refusing", m->state_file, m->name, name);
×
349

350
        if (id)
×
351
                (void) sd_id128_from_string(id, &m->id);
×
352

353
        parse_pid_and_pidfdid(&m->leader, leader, leader_pidfdid, "leader");
×
354
        parse_pid_and_pidfdid(&m->supervisor, supervisor, supervisor_pidfdid, "supervisor");
×
355

356
        if (class) {
×
357
                MachineClass c = machine_class_from_string(class);
×
358
                if (c >= 0)
×
359
                        m->class = c;
×
360
        }
361

362
        if (realtime)
×
363
                (void) deserialize_usec(realtime, &m->timestamp.realtime);
×
364
        if (monotonic)
×
365
                (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
×
366

367
        m->netif = mfree(m->netif);
×
368
        m->n_netif = 0;
×
369
        if (netif) {
×
370
                _cleanup_free_ int *ni = NULL;
×
371
                size_t nr = 0;
×
372

373
                for (const char *p = netif;;) {
×
374
                        _cleanup_free_ char *word = NULL;
×
375

376
                        r = extract_first_word(&p, &word, NULL, 0);
×
377
                        if (r == 0)
×
378
                                break;
379
                        if (r == -ENOMEM)
×
380
                                return log_oom();
×
381
                        if (r < 0) {
×
382
                                log_warning_errno(r, "Failed to parse NETIF: %s", netif);
×
383
                                break;
384
                        }
385

386
                        r = parse_ifindex(word);
×
387
                        if (r < 0)
×
388
                                continue;
×
389

390
                        if (!GREEDY_REALLOC(ni, nr + 1))
×
391
                                return log_oom();
×
392

393
                        ni[nr++] = r;
×
394
                }
395

396
                m->netif = TAKE_PTR(ni);
×
397
                m->n_netif = nr;
×
398
        }
399

400
        m->vsock_cid = 0;
×
401
        if (vsock_cid) {
×
402
                r = safe_atou(vsock_cid, &m->vsock_cid);
×
403
                if (r < 0)
×
404
                        log_warning_errno(r, "Failed to parse AF_VSOCK CID, ignoring: %s", vsock_cid);
×
405
        }
406

407
        if (uid) {
×
408
                r = parse_uid(uid, &m->uid);
×
409
                if (r < 0)
×
410
                        log_warning_errno(r, "Failed to parse owning UID, ignoring: %s", uid);
×
411
        }
412

413
        return r;
414
}
415

416
static int machine_start_scope(
×
417
                Machine *machine,
418
                bool allow_pidfd,
419
                sd_bus_message *more_properties,
420
                sd_bus_error *error) {
421

422
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
×
423
        _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
×
424
        _cleanup_free_ char *escaped = NULL, *unit = NULL;
×
425
        const char *description;
×
426
        int r;
×
427

428
        assert(machine);
×
429
        assert(pidref_is_set(&machine->leader));
×
430
        assert(!machine->unit);
×
431
        assert(!machine->subgroup);
×
432

433
        escaped = unit_name_escape(machine->name);
×
434
        if (!escaped)
×
435
                return log_oom();
×
436

437
        unit = strjoin("machine-", escaped, ".scope");
×
438
        if (!unit)
×
439
                return log_oom();
×
440

441
        r = bus_message_new_method_call(
×
442
                        machine->manager->api_bus,
×
443
                        &m,
444
                        bus_systemd_mgr,
445
                        "StartTransientUnit");
446
        if (r < 0)
×
447
                return r;
448

449
        r = sd_bus_message_append(m, "ss", unit, "fail");
×
450
        if (r < 0)
×
451
                return r;
452

453
        r = sd_bus_message_open_container(m, 'a', "(sv)");
×
454
        if (r < 0)
×
455
                return r;
456

457
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
×
458
        if (r < 0)
×
459
                return r;
460

461
        description = strjoina(machine->class == MACHINE_VM ? "Virtual Machine " : "Container ", machine->name);
×
462
        r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
×
463
        if (r < 0)
×
464
                return r;
465

466
        r = bus_append_scope_pidref(m, &machine->leader, allow_pidfd);
×
467
        if (r < 0)
×
468
                return r;
469

470
        r = sd_bus_message_append(
×
471
                        m, "(sv)(sv)(sv)(sv)",
472
                        "Delegate", "b", 1,
473
                        "CollectMode", "s", "inactive-or-failed",
474
                        "AddRef", "b", 1,
475
                        "TasksMax", "t", UINT64_C(16384));
476
        if (r < 0)
×
477
                return r;
478

479
        if (machine->uid != 0) {
×
480
                _cleanup_free_ char *u = NULL;
×
481

482
                if (asprintf(&u, UID_FMT, machine->uid) < 0)
×
483
                        return -ENOMEM;
484

485
                r = sd_bus_message_append(
×
486
                                m, "(sv)",
487
                                "User", "s", u);
488
                if (r < 0)
×
489
                        return r;
490
        }
491

492
        if (more_properties) {
×
493
                r = sd_bus_message_copy(m, more_properties, true);
×
494
                if (r < 0)
×
495
                        return r;
496
        }
497

498
        r = sd_bus_message_close_container(m);
×
499
        if (r < 0)
×
500
                return r;
501

502
        r = sd_bus_message_append(m, "a(sa(sv))", 0);
×
503
        if (r < 0)
×
504
                return r;
505

506
        r = sd_bus_call(NULL, m, 0, &e, &reply);
×
507
        if (r < 0) {
×
508
                /* If this failed with a property we couldn't write, this is quite likely because the server
509
                 * doesn't support PIDFDs yet, let's try without. */
510
                if (allow_pidfd &&
×
511
                    sd_bus_error_has_names(&e, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
×
512
                        return machine_start_scope(machine, /* allow_pidfd= */ false, more_properties, error);
×
513

514
                return sd_bus_error_move(error, &e);
×
515
        }
516

517
        machine->unit = TAKE_PTR(unit);
×
518
        machine->referenced = true;
×
519

520
        const char *job;
×
521
        r = sd_bus_message_read(reply, "o", &job);
×
522
        if (r < 0)
×
523
                return r;
524

525
        return free_and_strdup(&machine->scope_job, job);
×
526
}
527

528
static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
53✔
529
        int r;
53✔
530

531
        assert(m);
53✔
532
        assert(m->class != MACHINE_HOST);
53✔
533

534
        if (!m->unit) {
53✔
535
                r = machine_start_scope(m, /* allow_pidfd= */ true, properties, error);
×
536
                if (r < 0)
×
537
                        return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
×
538
        }
539

540
        assert(m->unit);
53✔
541

542
        if (!m->subgroup) {
53✔
543
                r = hashmap_ensure_put(&m->manager->machines_by_unit, &string_hash_ops, m->unit, m);
28✔
544
                if (r < 0)
28✔
545
                        return r;
×
546
        }
547

548
        return 0;
549
}
550

551
static int machine_dispatch_leader_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
49✔
552
        Machine *m = ASSERT_PTR(userdata);
49✔
553

554
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
49✔
555
        machine_add_to_gc_queue(m);
49✔
556

557
        return 0;
49✔
558
}
559

560
static int machine_dispatch_supervisor_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
1✔
561
        Machine *m = ASSERT_PTR(userdata);
1✔
562

563
        m->supervisor_pidfd_event_source = sd_event_source_disable_unref(m->supervisor_pidfd_event_source);
1✔
564
        machine_add_to_gc_queue(m);
1✔
565

566
        return 0;
1✔
567
}
568

569
static int machine_watch_pidfd(Machine *m, PidRef *pidref, sd_event_source **source, sd_event_io_handler_t cb) {
106✔
570
        int r;
106✔
571

572
        assert(m);
106✔
573
        assert(m->manager);
106✔
574
        assert(source);
106✔
575
        assert(!*source);
106✔
576
        assert(cb);
106✔
577

578
        if (!pidref_is_set(pidref) || pidref->fd < 0)
210✔
579
                return 0;
580

581
        /* If we have a pidfd for the leader or supervisor, let's also track it for POLLIN, and GC the machine
582
         * automatically if it dies */
583

584
        r = sd_event_add_io(m->manager->event, source, pidref->fd, EPOLLIN, cb, m);
104✔
585
        if (r < 0)
104✔
586
                return r;
587

588
        (void) sd_event_source_set_description(*source, "machine-pidfd");
104✔
589

590
        return 0;
104✔
591
}
592

593
static int machine_dispatch_cgroup_empty(sd_event_source *s, const struct inotify_event *event, void *userdata) {
19✔
594
        Machine *m = ASSERT_PTR(userdata);
19✔
595
        int r;
19✔
596

597
        assert(m->cgroup);
19✔
598

599
        r = cg_is_empty(m->cgroup);
19✔
600
        if (r < 0)
19✔
601
                return log_error_errno(r, "Failed to determine if cgroup '%s' is empty: %m", m->cgroup);
×
602

603
        if (r > 0)
19✔
604
                machine_add_to_gc_queue(m);
19✔
605

606
        return 0;
607
}
608

609
static int machine_watch_cgroup(Machine *m) {
53✔
610
        int r;
53✔
611

612
        assert(m);
53✔
613
        assert(!m->cgroup_empty_event_source);
53✔
614

615
        if (!m->cgroup)
53✔
616
                return 0;
53✔
617

618
        _cleanup_free_ char *p = NULL;
25✔
619
        r = cg_get_path(m->cgroup, "cgroup.events", &p);
25✔
620
        if (r < 0)
25✔
621
                return log_error_errno(r, "Failed to get cgroup path for cgroup '%s': %m", m->cgroup);
×
622

623
        r = sd_event_add_inotify(m->manager->event, &m->cgroup_empty_event_source, p, IN_MODIFY, machine_dispatch_cgroup_empty, m);
25✔
624
        if (r < 0)
25✔
625
                return log_error_errno(r, "Failed to watch %s events: %m", p);
×
626

627
        return 0;
628
}
629

630
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
61✔
631
        int r;
61✔
632

633
        assert(m);
61✔
634

635
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
61✔
636
                return -EOPNOTSUPP;
637

638
        if (m->started)
53✔
639
                return 0;
640

641
        r = hashmap_ensure_put(&m->manager->machines_by_leader, &pidref_hash_ops, &m->leader, m);
53✔
642
        if (r < 0)
53✔
643
                return r;
644

645
        r = machine_watch_pidfd(m, &m->leader, &m->leader_pidfd_event_source, machine_dispatch_leader_pidfd);
53✔
646
        if (r < 0)
53✔
647
                return r;
648

649
        r = machine_watch_pidfd(m, &m->supervisor, &m->supervisor_pidfd_event_source, machine_dispatch_supervisor_pidfd);
53✔
650
        if (r < 0)
53✔
651
                return r;
652

653
        r = machine_watch_cgroup(m);
53✔
654
        if (r < 0)
53✔
655
                return r;
656

657
        /* Create cgroup */
658
        r = machine_ensure_scope(m, properties, error);
53✔
659
        if (r < 0)
53✔
660
                return r;
661

662
        log_struct(LOG_INFO,
53✔
663
                   LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START_STR),
664
                   LOG_ITEM("NAME=%s", m->name),
665
                   LOG_ITEM("LEADER="PID_FMT, m->leader.pid),
666
                   LOG_MESSAGE("New machine %s.", m->name));
667

668
        if (!dual_timestamp_is_set(&m->timestamp))
53✔
669
                dual_timestamp_now(&m->timestamp);
53✔
670

671
        m->started = true;
53✔
672

673
        /* Save new machine data */
674
        machine_save(m);
53✔
675

676
        machine_send_signal(m, "MachineNew");
53✔
677

678
        (void) manager_notify_hook_filters(m->manager);
53✔
679

680
        return 0;
53✔
681
}
682

683
int machine_stop(Machine *m) {
43✔
684
        int r;
43✔
685

686
        assert(m);
43✔
687

688
        log_debug("Stopping machine '%s'.", m->name);
43✔
689

690
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
43✔
691
                return -EOPNOTSUPP;
692

693
        if (m->unit && !m->subgroup) {
68✔
694
                /* If the machine runs as its own unit, then we'll terminate that */
695
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
696
                char *job = NULL;
25✔
697

698
                r = manager_stop_unit(m->manager, m->unit, &error, &job);
25✔
699
                if (r < 0)
25✔
700
                        return log_error_errno(r, "Failed to stop machine unit: %s", bus_error_message(&error, r));
×
701

702
                free_and_replace(m->scope_job, job);
25✔
703

704
        } else if (pidref_is_set(&m->supervisor)) {
705
                /* Otherwise, send a friendly SIGTERM to the supervisor */
706
                r = pidref_kill(&m->supervisor, SIGTERM);
18✔
707
                if (r < 0)
18✔
708
                        return log_error_errno(r, "Failed to kill supervisor process " PID_FMT " of machine '%s': %m", m->supervisor.pid, m->name);
×
709
        } else
710
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Don't know how to terminate machine '%s'.", m->name);
×
711

712
        m->stopping = true;
43✔
713

714
        machine_save(m);
43✔
715

716
        return 0;
43✔
717
}
718

719
int machine_finalize(Machine *m) {
61✔
720
        assert(m);
61✔
721

722
        if (m->started) {
61✔
723
                log_struct(LOG_INFO,
50✔
724
                           LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP_STR),
725
                           LOG_ITEM("NAME=%s", m->name),
726
                           LOG_ITEM("LEADER="PID_FMT, m->leader.pid),
727
                           LOG_MESSAGE("Machine %s terminated.", m->name));
728

729
                m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
50✔
730
        }
731

732
        machine_unlink(m);
61✔
733
        machine_add_to_gc_queue(m);
61✔
734

735
        if (m->started) {
61✔
736
                machine_send_signal(m, "MachineRemoved");
50✔
737
                m->started = false;
50✔
738

739
                (void) manager_notify_hook_filters(m->manager);
50✔
740
        }
741

742
        return 0;
61✔
743
}
744

745
bool machine_may_gc(Machine *m, bool drop_not_started) {
194✔
746
        int r;
194✔
747

748
        assert(m);
194✔
749

750
        if (m->class == MACHINE_HOST)
194✔
751
                return false;
752

753
        if (drop_not_started && !m->started)
168✔
754
                return true;
755

756
        r = pidref_is_alive(&m->leader);
146✔
757
        if (r == -ESRCH)
146✔
758
                return true;
759
        if (r < 0)
146✔
760
                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", m->leader.pid);
×
761
        if (r > 0)
146✔
762
                return false;
763

764
        if (m->scope_job) {
97✔
765
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
17✔
766

767
                r = manager_job_is_active(m->manager, m->scope_job, &error);
28✔
768
                if (r < 0)
28✔
769
                        log_debug_errno(r, "Failed to determine whether job '%s' is active, assuming it is: %s", m->scope_job, bus_error_message(&error, r));
×
770
                if (r != 0)
28✔
771
                        return false;
11✔
772
        }
773

774
        if (m->unit && !m->subgroup) {
86✔
775
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
50✔
776

777
                r = manager_unit_is_active(m->manager, m->unit, &error);
55✔
778
                if (r < 0)
55✔
779
                        log_debug_errno(r, "Failed to determine whether unit '%s' is active, assuming it is: %s", m->unit, bus_error_message(&error, r));
×
780
                if (r != 0)
55✔
781
                        return false;
5✔
782
        }
783

784
        if (m->cgroup) {
81✔
785
                r = cg_is_empty(m->cgroup);
31✔
786
                if (IN_SET(r, 0, -ENOENT))
31✔
787
                        return true;
788
                if (r < 0)
31✔
789
                        log_debug_errno(r, "Failed to determine if cgroup '%s' is empty, ignoring: %m", m->cgroup);
194✔
790
        }
791

792
        return true;
793
}
794

795
void machine_add_to_gc_queue(Machine *m) {
212✔
796
        assert(m);
212✔
797

798
        if (m->in_gc_queue)
212✔
799
                return;
800

801
        LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
147✔
802
        m->in_gc_queue = true;
147✔
803

804
        manager_enqueue_gc(m->manager);
147✔
805
}
806

807
MachineState machine_get_state(Machine *m) {
88✔
808
        assert(m);
88✔
809

810
        if (m->class == MACHINE_HOST)
88✔
811
                return MACHINE_RUNNING;
812

813
        if (m->stopping)
88✔
814
                return MACHINE_CLOSING;
815

816
        if (m->scope_job)
71✔
817
                return MACHINE_OPENING;
×
818

819
        return MACHINE_RUNNING;
820
}
821

822
int machine_kill(Machine *m, KillWhom whom, int signo) {
15✔
823
        assert(m);
15✔
824

825
        log_debug("Killing machine '%s' (%s) with signal %s.", m->name, kill_whom_to_string(whom), signal_to_string(signo));
15✔
826

827
        if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
15✔
828
                return -EOPNOTSUPP;
829

830
        switch (whom) {
15✔
831

832
        case KILL_LEADER:
14✔
833
                return pidref_kill(&m->leader, signo);
14✔
834

835
        case KILL_SUPERVISOR:
×
836
                return pidref_kill(&m->supervisor, signo);
×
837

838
        case KILL_ALL:
1✔
839
                if (!m->unit)
1✔
840
                        return -ESRCH;
841

842
                return manager_kill_unit(m->manager, m->unit, m->subgroup, signo, /* error= */ NULL);
1✔
843

844
        default:
×
845
                assert_not_reached();
×
846
        }
847
}
848

849
int machine_openpt(Machine *m, int flags, char **ret_peer) {
8✔
850
        assert(m);
8✔
851

852
        switch (m->class) {
8✔
853

854
        case MACHINE_HOST:
8✔
855
                return openpt_allocate(flags, ret_peer);
8✔
856

857
        case MACHINE_CONTAINER:
858
                if (!pidref_is_set(&m->leader))
×
859
                        return -EINVAL;
860

861
                return openpt_allocate_in_namespace(&m->leader, flags, ret_peer);
×
862

863
        default:
864
                return -EOPNOTSUPP;
865
        }
866
}
867

868
static int machine_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
6✔
869
        int r;
6✔
870

871
        assert(m);
6✔
872
        assert(ret);
6✔
873

874
        switch (m->class) {
6✔
875

876
        case MACHINE_HOST:
6✔
877
                *ret = NULL;
6✔
878
                return 0;
6✔
879

880
        case MACHINE_CONTAINER: {
×
881
                _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
×
882
                char *address;
×
883

884
                r = sd_bus_new(&bus);
×
885
                if (r < 0)
×
886
                        return log_debug_errno(r, "Failed to allocate new DBus: %m");
×
887

888
                if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader.pid) < 0)
×
889
                        return -ENOMEM;
890

891
                bus->address = address;
×
892
                bus->bus_client = true;
×
893
                bus->trusted = false;
×
894
                bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
×
895

896
                r = sd_bus_start(bus);
×
897
                if (r == -ENOENT)
×
898
                        return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
×
899
                if (r < 0)
×
900
                        return r;
901

902
                *ret = TAKE_PTR(bus);
×
903
                return 0;
×
904
        }
905

906
        default:
907
                return -EOPNOTSUPP;
908
        }
909
}
910

911
int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error) {
1✔
912
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
1✔
913
        sd_bus *container_bus = NULL;
1✔
914
        const char *p, *getty;
1✔
915
        int r;
1✔
916

917
        assert(m);
1✔
918
        assert(ptmx_name);
1✔
919

920
        p = path_startswith(ptmx_name, "/dev/pts/");
1✔
921
        if (!p)
1✔
922
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
923

924
        r = machine_bus_new(m, error, &allocated_bus);
1✔
925
        if (r < 0)
1✔
926
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
927

928
        container_bus = allocated_bus ?: m->manager->system_bus;
1✔
929
        getty = strjoina("container-getty@", p, ".service");
7✔
930

931
        r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, /* ret_reply= */ NULL, "ss", getty, "replace");
1✔
932
        if (r < 0)
1✔
933
                return log_debug_errno(r, "Failed to StartUnit '%s' in container '%s': %m", getty, m->name);
×
934

935
        return 0;
936
}
937

938
int machine_start_shell(
5✔
939
                Machine *m,
940
                int ptmx_fd,
941
                const char *ptmx_name,
942
                const char *user,
943
                const char *path,
944
                char **args,
945
                char **env,
946
                sd_bus_error *error) {
947
        _cleanup_close_ int pty_fd = -EBADF;
5✔
948
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *tm = NULL;
5✔
949
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
5✔
950
        const char *p, *utmp_id, *unit, *description;
5✔
951
        sd_bus *container_bus = NULL;
5✔
952
        int r;
5✔
953

954
        assert(m);
5✔
955
        assert(ptmx_fd >= 0);
5✔
956
        assert(ptmx_name);
5✔
957

958
        if (isempty(user) || isempty(path) || strv_isempty(args))
20✔
959
                return -EINVAL;
960

961
        p = path_startswith(ptmx_name, "/dev/pts/");
5✔
962
        utmp_id = path_startswith(ptmx_name, "/dev/");
5✔
963
        if (!p || !utmp_id)
5✔
964
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
965

966
        pty_fd = pty_open_peer(ptmx_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
5✔
967
        if (pty_fd < 0)
5✔
968
                return log_debug_errno(pty_fd, "Failed to open terminal: %m");
×
969

970
        r = machine_bus_new(m, error, &allocated_bus);
5✔
971
        if (r < 0)
5✔
972
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
973

974
        container_bus = allocated_bus ?: m->manager->system_bus;
5✔
975
        r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
5✔
976
        if (r < 0)
5✔
977
                return r;
978

979
        /* Name and mode */
980
        unit = strjoina("container-shell@", p, ".service");
35✔
981
        r = sd_bus_message_append(tm, "ss", unit, "fail");
5✔
982
        if (r < 0)
5✔
983
                return r;
984

985
        /* Properties */
986
        r = sd_bus_message_open_container(tm, 'a', "(sv)");
5✔
987
        if (r < 0)
5✔
988
                return r;
989

990
        description = strjoina("Shell for User ", user);
25✔
991
        r = sd_bus_message_append(tm,
5✔
992
                                  "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
993
                                  "Description", "s", description,
994
                                  "StandardInputFileDescriptor", "h", pty_fd,
995
                                  "StandardOutputFileDescriptor", "h", pty_fd,
996
                                  "StandardErrorFileDescriptor", "h", pty_fd,
997
                                  "SendSIGHUP", "b", true,
998
                                  "IgnoreSIGPIPE", "b", false,
999
                                  "KillMode", "s", "mixed",
1000
                                  "TTYPath", "s", ptmx_name,
1001
                                  "TTYReset", "b", true,
1002
                                  "UtmpIdentifier", "s", utmp_id,
1003
                                  "UtmpMode", "s", "user",
1004
                                  "PAMName", "s", "login",
1005
                                  "WorkingDirectory", "s", "-~");
1006
        if (r < 0)
5✔
1007
                return r;
1008

1009
        r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
5✔
1010
        if (r < 0)
5✔
1011
                return r;
1012

1013
        if (!strv_isempty(env)) {
5✔
1014
                r = sd_bus_message_open_container(tm, 'r', "sv");
4✔
1015
                if (r < 0)
4✔
1016
                        return r;
1017

1018
                r = sd_bus_message_append(tm, "s", "Environment");
4✔
1019
                if (r < 0)
4✔
1020
                        return r;
1021

1022
                r = sd_bus_message_open_container(tm, 'v', "as");
4✔
1023
                if (r < 0)
4✔
1024
                        return r;
1025

1026
                r = sd_bus_message_append_strv(tm, env);
4✔
1027
                if (r < 0)
4✔
1028
                        return r;
1029

1030
                r = sd_bus_message_close_container(tm);
4✔
1031
                if (r < 0)
4✔
1032
                        return r;
1033

1034
                r = sd_bus_message_close_container(tm);
4✔
1035
                if (r < 0)
4✔
1036
                        return r;
1037
        }
1038

1039
        /* Exec container */
1040
        r = sd_bus_message_open_container(tm, 'r', "sv");
5✔
1041
        if (r < 0)
5✔
1042
                return r;
1043

1044
        r = sd_bus_message_append(tm, "s", "ExecStart");
5✔
1045
        if (r < 0)
5✔
1046
                return r;
1047

1048
        r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
5✔
1049
        if (r < 0)
5✔
1050
                return r;
1051

1052
        r = sd_bus_message_open_container(tm, 'a', "(sasb)");
5✔
1053
        if (r < 0)
5✔
1054
                return r;
1055

1056
        r = sd_bus_message_open_container(tm, 'r', "sasb");
5✔
1057
        if (r < 0)
5✔
1058
                return r;
1059

1060
        r = sd_bus_message_append(tm, "s", path);
5✔
1061
        if (r < 0)
5✔
1062
                return r;
1063

1064
        r = sd_bus_message_append_strv(tm, args);
5✔
1065
        if (r < 0)
5✔
1066
                return r;
1067

1068
        r = sd_bus_message_append(tm, "b", true);
5✔
1069
        if (r < 0)
5✔
1070
                return r;
1071

1072
        r = sd_bus_message_close_container(tm);
5✔
1073
        if (r < 0)
5✔
1074
                return r;
1075

1076
        r = sd_bus_message_close_container(tm);
5✔
1077
        if (r < 0)
5✔
1078
                return r;
1079

1080
        r = sd_bus_message_close_container(tm);
5✔
1081
        if (r < 0)
5✔
1082
                return r;
1083

1084
        r = sd_bus_message_close_container(tm);
5✔
1085
        if (r < 0)
5✔
1086
                return r;
1087

1088
        r = sd_bus_message_close_container(tm);
5✔
1089
        if (r < 0)
5✔
1090
                return r;
1091

1092
        /* Auxiliary units */
1093
        r = sd_bus_message_append(tm, "a(sa(sv))", 0);
5✔
1094
        if (r < 0)
5✔
1095
                return r;
1096

1097
        r = sd_bus_call(container_bus, tm, 0, error, NULL);
5✔
1098
        if (r < 0)
5✔
1099
                return r;
×
1100

1101
        return 0;
1102
}
1103

1104
char** machine_default_shell_args(const char *user) {
1✔
1105
        _cleanup_strv_free_ char **args = NULL;
1✔
1106
        int r;
1✔
1107

1108
        assert(user);
1✔
1109

1110
        args = new0(char*, 3 + 1);
1✔
1111
        if (!args)
1✔
1112
                return NULL;
1113

1114
        args[0] = strdup("sh");
1✔
1115
        if (!args[0])
1✔
1116
                return NULL;
1117

1118
        args[1] = strdup("-c");
1✔
1119
        if (!args[1])
1✔
1120
                return NULL;
1121

1122
        r = asprintf(&args[2],
1✔
1123
                     "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
1124
                     "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
1125
                     user);
1126
        if (r < 0) {
1✔
1127
                args[2] = NULL;
×
1128
                return NULL;
×
1129
        }
1130

1131
        return TAKE_PTR(args);
1✔
1132
}
1133

1134
int machine_copy_from_to_operation(
8✔
1135
                Manager *manager,
1136
                Machine *machine,
1137
                const char *host_path,
1138
                const char *container_path,
1139
                bool copy_from_container,
1140
                CopyFlags copy_flags,
1141
                Operation **ret) {
1142

1143
        _cleanup_close_ int host_fd = -EBADF, target_mntns_fd = -EBADF, source_mntns_fd = -EBADF;
16✔
1144
        _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
×
1145
        _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
8✔
1146
        _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
8✔
1147
        uid_t uid_shift;
8✔
1148
        int r;
8✔
1149

1150
        assert(manager);
8✔
1151
        assert(machine);
8✔
1152
        assert(ret);
8✔
1153

1154
        if (isempty(host_path) || isempty(container_path))
24✔
1155
                return -EINVAL;
1156

1157
        r = path_extract_filename(host_path, &host_basename);
8✔
1158
        if (r < 0)
8✔
1159
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", host_path);
×
1160

1161
        r = path_extract_filename(container_path, &container_basename);
8✔
1162
        if (r < 0)
8✔
1163
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path);
×
1164

1165
        host_fd = open_parent(host_path, O_CLOEXEC, 0);
8✔
1166
        if (host_fd < 0)
8✔
1167
                return log_debug_errno(host_fd, "Failed to open host directory '%s': %m", host_path);
×
1168

1169
        r = machine_get_uid_shift(machine, &uid_shift);
8✔
1170
        if (r < 0)
8✔
1171
                return log_debug_errno(r, "Failed to get UID shift of machine '%s': %m", machine->name);
×
1172

1173
        target_mntns_fd = pidref_namespace_open_by_type(&machine->leader, NAMESPACE_MOUNT);
8✔
1174
        if (target_mntns_fd < 0)
8✔
1175
                return log_debug_errno(target_mntns_fd, "Failed to open mount namespace of machine '%s': %m", machine->name);
×
1176

1177
        source_mntns_fd = namespace_open_by_type(NAMESPACE_MOUNT);
8✔
1178
        if (source_mntns_fd < 0)
8✔
1179
                return log_debug_errno(source_mntns_fd, "Failed to open our own mount namespace: %m");
×
1180

1181
        if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
8✔
1182
                return log_debug_errno(errno, "Failed to create pipe: %m");
×
1183

1184
        r = namespace_fork("(sd-copyns)",
8✔
1185
                           "(sd-copy)",
1186
                           FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
1187
                           /* pidns_fd= */ -EBADF,
1188
                           target_mntns_fd,
1189
                           /* netns_fd= */ -EBADF,
1190
                           /* userns_fd= */ -EBADF,
1191
                           /* root_fd= */ -EBADF,
1192
                           &child);
1193
        if (r < 0)
16✔
1194
                return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
×
1195
        if (r == 0) {
16✔
1196
                errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
8✔
1197

1198
                _cleanup_close_ int container_fd = -EBADF;
×
1199
                container_fd = open_parent(container_path, O_CLOEXEC, 0);
8✔
1200
                if (container_fd < 0) {
8✔
1201
                        log_debug_errno(container_fd, "Failed to open container directory: %m");
×
1202
                        report_errno_and_exit(errno_pipe_fd[1], container_fd);
×
1203
                }
1204

1205
                /* Rejoin the host namespace, so that /proc/self/fd/… works, which copy_tree_at() relies on
1206
                 * in some cases (by means of fd_reopen()) */
1207
                if (setns(source_mntns_fd, CLONE_NEWNS) < 0) {
8✔
1208
                        r = log_debug_errno(errno, "Failed to rejoin namespace of host: %m");
×
1209
                        report_errno_and_exit(errno_pipe_fd[1], r);
×
1210
                }
1211

1212
                /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to
1213
                 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1214
                 * the UID/GIDs as they are. */
1215
                if (copy_from_container)
8✔
1216
                        r = copy_tree_at(
2✔
1217
                                        container_fd,
1218
                                        container_basename,
1219
                                        host_fd,
1220
                                        host_basename,
1221
                                        uid_shift == 0 ? UID_INVALID : 0,
1222
                                        uid_shift == 0 ? GID_INVALID : 0,
2✔
1223
                                        copy_flags,
1224
                                        /* denylist= */ NULL,
1225
                                        /* subvolumes= */ NULL);
1226
                else
1227
                        r = copy_tree_at(
6✔
1228
                                        host_fd,
1229
                                        host_basename,
1230
                                        container_fd,
1231
                                        container_basename,
1232
                                        uid_shift == 0 ? UID_INVALID : uid_shift,
1233
                                        uid_shift == 0 ? GID_INVALID : uid_shift,
6✔
1234
                                        copy_flags,
1235
                                        /* denylist= */ NULL,
1236
                                        /* subvolumes= */ NULL);
1237
                if (r < 0)
8✔
1238
                        log_debug_errno(r, "Failed to copy tree: %m");
1✔
1239

1240
                report_errno_and_exit(errno_pipe_fd[1], r);
8✔
1241
        }
1242

1243
        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
8✔
1244

1245
        Operation *operation;
8✔
1246
        r = operation_new(manager, machine, &child, errno_pipe_fd[0], &operation);
8✔
1247
        if (r < 0)
8✔
1248
                return r;
1249

1250
        TAKE_FD(errno_pipe_fd[0]);
8✔
1251
        TAKE_PIDREF(child);
8✔
1252

1253
        *ret = operation;
8✔
1254
        return 0;
8✔
1255
}
1256

1257
void machine_release_unit(Machine *m) {
61✔
1258
        assert(m);
61✔
1259

1260
        if (!m->unit)
61✔
1261
                return;
1262

1263
        assert(m->manager);
61✔
1264

1265
        if (m->referenced) {
61✔
1266
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1267
                int r;
×
1268

1269
                r = manager_unref_unit(m->manager, m->unit, &error);
×
1270
                if (r < 0)
×
1271
                        log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
×
1272
                                       "Failed to drop reference to machine scope, ignoring: %s",
1273
                                       bus_error_message(&error, r));
1274

1275
                m->referenced = false;
×
1276
        }
1277

1278
        if (!m->subgroup)
61✔
1279
                (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
36✔
1280

1281
        m->unit = mfree(m->unit);
61✔
1282

1283
        /* Also free the subgroup, because it only makes sense in the context of the unit */
1284
        m->subgroup = mfree(m->subgroup);
61✔
1285
}
1286

1287
int machine_get_uid_shift(Machine *m, uid_t *ret) {
46✔
1288
        char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
46✔
1289
        uid_t uid_base, uid_shift, uid_range;
46✔
1290
        gid_t gid_base, gid_shift, gid_range;
46✔
1291
        _cleanup_fclose_ FILE *f = NULL;
46✔
1292
        int r;
46✔
1293

1294
        assert(m);
46✔
1295
        assert(ret);
46✔
1296

1297
        /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
1298
         * mappings. In most cases setups should be simple like this, and administrators should only care about the
1299
         * basic offset a container has relative to the host. This is what this function exposes.
1300
         *
1301
         * If we encounter any more complex mappings we politely refuse this with ENXIO. */
1302

1303
        if (m->class == MACHINE_HOST) {
46✔
1304
                *ret = 0;
5✔
1305
                return 0;
5✔
1306
        }
1307

1308
        if (m->class != MACHINE_CONTAINER)
41✔
1309
                return -EOPNOTSUPP;
1310

1311
        xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader.pid);
41✔
1312
        f = fopen(p, "re");
41✔
1313
        if (!f) {
41✔
1314
                if (errno == ENOENT) {
×
1315
                        /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
1316
                        *ret = 0;
×
1317
                        return 0;
×
1318
                }
1319

1320
                return -errno;
×
1321
        }
1322

1323
        /* Read the first line. There's at least one. */
1324
        r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
41✔
1325
        if (r < 0)
41✔
1326
                return r;
1327

1328
        /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
1329
        if (uid_base != 0)
41✔
1330
                return -ENXIO;
1331
        /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
1332
        if (uid_range < UID_NOBODY)
41✔
1333
                return -ENXIO;
1334

1335
        /* If there's more than one line, then we don't support this mapping. */
1336
        r = safe_fgetc(f, NULL);
41✔
1337
        if (r < 0)
41✔
1338
                return r;
1339
        if (r != 0) /* Insist on EOF */
41✔
1340
                return -ENXIO;
1341

1342
        fclose(f);
41✔
1343

1344
        xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
41✔
1345
        f = fopen(p, "re");
41✔
1346
        if (!f)
41✔
1347
                return -errno;
×
1348

1349
        /* Read the first line. There's at least one. */
1350
        errno = 0;
41✔
1351
        r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
41✔
1352
        if (r == EOF)
41✔
1353
                return errno_or_else(ENOMSG);
×
1354
        assert(r >= 0);
41✔
1355
        if (r != 3)
41✔
1356
                return -EBADMSG;
1357

1358
        /* If there's more than one line, then we don't support this file. */
1359
        r = safe_fgetc(f, NULL);
41✔
1360
        if (r < 0)
41✔
1361
                return r;
1362
        if (r != 0) /* Insist on EOF */
41✔
1363
                return -ENXIO;
1364

1365
        /* If the UID and GID mapping doesn't match, we don't support this mapping. */
1366
        if (uid_base != (uid_t) gid_base)
41✔
1367
                return -ENXIO;
1368
        if (uid_shift != (uid_t) gid_shift)
41✔
1369
                return -ENXIO;
1370
        if (uid_range != (uid_t) gid_range)
41✔
1371
                return -ENXIO;
1372

1373
        r = pidref_verify(&m->leader);
41✔
1374
        if (r < 0)
41✔
1375
                return r;
1376

1377
        *ret = uid_shift;
41✔
1378
        return 0;
41✔
1379
}
1380

1381
static int machine_owns_uid_internal(
378✔
1382
                Machine *machine,
1383
                const char *map_file, /* "uid_map" or "gid_map" */
1384
                uid_t uid,
1385
                uid_t *ret_internal_uid) {
1386

1387
        _cleanup_fclose_ FILE *f = NULL;
378✔
1388
        const char *p;
378✔
1389
        int r;
378✔
1390

1391
        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
1392
        assert_cc(sizeof(uid_t) == sizeof(gid_t));
378✔
1393

1394
        assert(machine);
378✔
1395

1396
        /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
1397
         * internally in the machine */
1398

1399
        if (machine->class != MACHINE_CONTAINER)
378✔
1400
                goto negative;
306✔
1401

1402
        p = procfs_file_alloca(machine->leader.pid, map_file);
72✔
1403
        f = fopen(p, "re");
72✔
1404
        if (!f) {
72✔
1405
                log_debug_errno(errno, "Failed to open %s, ignoring.", p);
×
1406
                goto negative;
×
1407
        }
1408

1409
        for (;;) {
212✔
1410
                uid_t uid_base, uid_shift, uid_range, converted;
142✔
1411

1412
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
142✔
1413
                if (r == -ENOMSG)
142✔
1414
                        break;
1415
                if (r < 0)
72✔
1416
                        return r;
2✔
1417

1418
                /* The private user namespace is disabled, ignoring. */
1419
                if (uid_shift == 0)
72✔
1420
                        continue;
70✔
1421

1422
                if (uid < uid_shift || uid >= uid_shift + uid_range)
20✔
1423
                        continue;
18✔
1424

1425
                converted = (uid - uid_shift + uid_base);
2✔
1426
                if (!uid_is_valid(converted))
2✔
1427
                        return -EINVAL;
1428

1429
                r = pidref_verify(&machine->leader);
2✔
1430
                if (r < 0)
2✔
1431
                        return r;
1432

1433
                if (ret_internal_uid)
2✔
1434
                        *ret_internal_uid = converted;
2✔
1435

1436
                return true;
1437
        }
1438

1439
negative:
376✔
1440
        if (ret_internal_uid)
376✔
1441
                *ret_internal_uid = UID_INVALID;
376✔
1442

1443
        return false;
1444
}
1445

1446
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
186✔
1447
        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
186✔
1448
}
1449

1450
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
192✔
1451
        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
192✔
1452
}
1453

1454
static int machine_translate_uid_internal(
4✔
1455
                Machine *machine,
1456
                const char *map_file, /* "uid_map" or "gid_map" */
1457
                uid_t uid,
1458
                uid_t *ret_host_uid) {
1459

1460
        _cleanup_fclose_ FILE *f = NULL;
4✔
1461
        const char *p;
4✔
1462
        int r;
4✔
1463

1464
        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
1465
        assert_cc(sizeof(uid_t) == sizeof(gid_t));
4✔
1466

1467
        assert(machine);
4✔
1468
        assert(uid_is_valid(uid));
4✔
1469

1470
        if (machine->class != MACHINE_CONTAINER)
4✔
1471
                return -ESRCH;
1472

1473
        /* Translates a machine UID into a host UID */
1474

1475
        p = procfs_file_alloca(machine->leader.pid, map_file);
4✔
1476
        f = fopen(p, "re");
4✔
1477
        if (!f)
4✔
1478
                return -errno;
×
1479

1480
        for (;;) {
4✔
1481
                uid_t uid_base, uid_shift, uid_range, converted;
4✔
1482

1483
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
4✔
1484
                if (r == -ENOMSG)
4✔
1485
                        break;
1486
                if (r < 0)
4✔
1487
                        return r;
4✔
1488

1489
                if (uid < uid_base || uid >= uid_base + uid_range)
4✔
1490
                        continue;
×
1491

1492
                converted = uid - uid_base + uid_shift;
4✔
1493
                if (!uid_is_valid(converted))
4✔
1494
                        return -EINVAL;
1495

1496
                r = pidref_verify(&machine->leader);
4✔
1497
                if (r < 0)
4✔
1498
                        return r;
1499

1500
                if (ret_host_uid)
4✔
1501
                        *ret_host_uid = converted;
4✔
1502

1503
                return 0;
1504
        }
1505

1506
        return -ESRCH;
×
1507
}
1508

1509
int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
2✔
1510
        return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
2✔
1511
}
1512

1513
int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
2✔
1514
        return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
2✔
1515
}
1516

1517
int machine_open_root_directory(Machine *machine) {
3✔
1518
        int r;
3✔
1519

1520
        assert(machine);
3✔
1521

1522
        switch (machine->class) {
3✔
1523
        case MACHINE_HOST: {
2✔
1524
                int fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
2✔
1525
                if (fd < 0)
2✔
1526
                        return log_debug_errno(errno, "Failed to open host root directory: %m");
×
1527

1528
                return fd;
1529
        }
1530

1531
        case MACHINE_CONTAINER: {
1✔
1532
                _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
1✔
1533
                _cleanup_(pidref_done) PidRef child = PIDREF_NULL;
×
1534
                _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
2✔
1535

1536
                r = pidref_namespace_open(&machine->leader,
1✔
1537
                                          /* ret_pidns_fd= */ NULL,
1538
                                          &mntns_fd,
1539
                                          /* ret_netns_fd= */ NULL,
1540
                                          /* ret_userns_fd= */ NULL,
1541
                                          &root_fd);
1542
                if (r < 0)
1✔
1543
                        return log_debug_errno(r, "Failed to open mount namespace of machine '%s': %m", machine->name);
×
1544

1545
                if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
1✔
1546
                        return log_debug_errno(errno, "Failed to open pipe: %m");
×
1547

1548
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, fd_pass_socket) < 0)
1✔
1549
                        return log_debug_errno(errno, "Failed to create socket pair: %m");
×
1550

1551
                r = namespace_fork(
1✔
1552
                                "(sd-openrootns)",
1553
                                "(sd-openroot)",
1554
                                FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
1555
                                /* pidns_fd= */  -EBADF,
1556
                                mntns_fd,
1557
                                /* netns_fd= */  -EBADF,
1558
                                /* userns_fd= */ -EBADF,
1559
                                root_fd,
1560
                                &child);
1561
                if (r < 0)
2✔
1562
                        return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
×
1563
                if (r == 0) {
2✔
1564
                        _cleanup_close_ int dfd = -EBADF;
×
1565

1566
                        errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1✔
1567
                        fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
1✔
1568

1569
                        dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1✔
1570
                        if (dfd < 0) {
1✔
1571
                                log_debug_errno(errno, "Failed to open root directory of machine '%s': %m", machine->name);
×
1572
                                report_errno_and_exit(errno_pipe_fd[1], -errno);
×
1573
                        }
1574

1575
                        r = send_one_fd(fd_pass_socket[1], dfd, /* flags= */ 0);
1✔
1576
                        dfd = safe_close(dfd);
1✔
1577
                        if (r < 0) {
1✔
1578
                                log_debug_errno(r, "Failed to send FD over socket: %m");
×
1579
                                report_errno_and_exit(errno_pipe_fd[1], r);
×
1580
                        }
1581

1582
                        _exit(EXIT_SUCCESS);
1✔
1583
                }
1584

1585
                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1✔
1586
                fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
1✔
1587

1588
                r = pidref_wait_for_terminate_and_check("(sd-openrootns)", &child, /* flags= */ 0);
1✔
1589
                if (r < 0)
1✔
1590
                        return log_debug_errno(r, "Failed to wait for child: %m");
×
1591

1592
                r = read_errno(errno_pipe_fd[0]); /* the function does debug reporting */
1✔
1593
                if (r < 0)
1✔
1594
                        return r;
1595

1596
                int fd = receive_one_fd(fd_pass_socket[0], MSG_DONTWAIT);
1✔
1597
                if (fd < 0)
1✔
1598
                        return log_debug_errno(fd, "Failed to receive FD from child: %m");
×
1599

1600
                return fd;
1601
        }
1602

1603
        default:
1604
                return -EOPNOTSUPP;
1605
        }
1606
}
1607

1608
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
1609
        [MACHINE_CONTAINER] = "container",
1610
        [MACHINE_VM]        = "vm",
1611
        [MACHINE_HOST]      = "host",
1612
};
1613

1614
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
330✔
1615

1616
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
1617
        [MACHINE_OPENING] = "opening",
1618
        [MACHINE_RUNNING] = "running",
1619
        [MACHINE_CLOSING] = "closing"
1620
};
1621

1622
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
87✔
1623

1624
static const char* const kill_whom_table[_KILL_WHOM_MAX] = {
1625
        [KILL_LEADER]     = "leader",
1626
        [KILL_SUPERVISOR] = "supervisor",
1627
        [KILL_ALL]        = "all",
1628
};
1629

1630
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
81✔
1631

1632
static const char* const acquire_metadata_table[_ACQUIRE_METADATA_MAX] = {
1633
        [ACQUIRE_METADATA_NO]       = "no",
1634
        [ACQUIRE_METADATA_YES]      = "yes",
1635
        [ACQUIRE_METADATA_GRACEFUL] = "graceful"
1636
};
1637

1638
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(acquire_metadata, AcquireMetadata, ACQUIRE_METADATA_YES);
9✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc