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

systemd / systemd / 20939507887

12 Jan 2026 11:33PM UTC coverage: 72.39% (-0.3%) from 72.693%
20939507887

push

github

yuwata
units: Fix the missing dependency in systemd-pcrproduct

NvPCR need to read from /var/lib/nvpcr and the current unit file is
executed before /var is mounted. This adds the dep back so its always
runs after /var is mounted like systemd-tpm2-setup.service

308905 of 426721 relevant lines covered (72.39%)

1143460.48 hits per line

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

71.68
/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) {
98✔
162
        int r;
98✔
163

164
        assert(m);
98✔
165

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

169
        if (!m->started)
98✔
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);
53✔
175
                if (!sl)
53✔
176
                        return log_oom();
×
177
        }
178

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

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

189
        if (fchmod(fileno(f), 0644) < 0)
98✔
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,
98✔
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);
98✔
201
        env_file_fputs_assignment(f, "SCOPE_JOB=", m->scope_job);
98✔
202

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

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

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

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

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

226
        if (dual_timestamp_is_set(&m->timestamp))
98✔
227
                fprintf(f,
98✔
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) {
98✔
234
                fputs("NETIF=\"", f);
44✔
235
                FOREACH_ARRAY(ifi, m->netif, m->n_netif) {
88✔
236
                        if (*ifi != 0)
44✔
237
                                fputc(' ', f);
44✔
238
                        fprintf(f, "%i", *ifi);
44✔
239
                }
240
                fputs("\"\n", f);
44✔
241
        }
242

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

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

249
        r = flink_tmpfile(f, temp_path, m->state_file, LINK_TMPFILE_REPLACE);
98✔
250
        if (r < 0)
98✔
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 */
98✔
254

255
        if (sl) {
98✔
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);
53✔
259

260
                /* disarm auto-removal */
261
                sl = mfree(sl);
53✔
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);
238✔
272
                (void) unlink(sl);
34✔
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) {
50✔
552
        Machine *m = ASSERT_PTR(userdata);
50✔
553

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

557
        return 0;
50✔
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) {
20✔
594
        Machine *m = ASSERT_PTR(userdata);
20✔
595
        int r;
20✔
596

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

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

603
        if (r > 0)
20✔
604
                machine_add_to_gc_queue(m);
20✔
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) {
66✔
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;
23✔
697

698
                r = manager_stop_unit(m->manager, m->unit, &error, &job);
23✔
699
                if (r < 0)
23✔
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);
23✔
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);
20✔
707
                if (r < 0)
20✔
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) {
198✔
746
        int r;
198✔
747

748
        assert(m);
198✔
749

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

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

756
        r = pidref_is_alive(&m->leader);
150✔
757
        if (r == -ESRCH)
150✔
758
                return true;
759
        if (r < 0)
150✔
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)
150✔
762
                return false;
763

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

767
                r = manager_job_is_active(m->manager, m->scope_job, &error);
23✔
768
                if (r < 0)
23✔
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)
23✔
771
                        return false;
11✔
772
        }
773

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

777
                r = manager_unit_is_active(m->manager, m->unit, &error);
53✔
778
                if (r < 0)
53✔
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)
53✔
781
                        return false;
5✔
782
        }
783

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

792
        return true;
793
}
794

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

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

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

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

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

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

813
        if (m->stopping)
89✔
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);
337✔
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