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

systemd / systemd / 19397511130

15 Nov 2025 09:47PM UTC coverage: 72.518% (+0.1%) from 72.37%
19397511130

push

github

web-flow
sd-event: several follow-ups for recent change (#39743)

3 of 4 new or added lines in 2 files covered. (75.0%)

1869 existing lines in 55 files now uncovered.

308519 of 425439 relevant lines covered (72.52%)

1258617.71 hits per line

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

71.93
/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) {
60✔
50
        _cleanup_(machine_freep) Machine *m = NULL;
60✔
51

52
        assert(class < _MACHINE_CLASS_MAX);
60✔
53
        assert(ret);
60✔
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);
60✔
60
        if (!m)
60✔
61
                return -ENOMEM;
62

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

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

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

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

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

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

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

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

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

103
        machine->manager = manager;
60✔
104

105
        return 0;
60✔
106
}
107

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

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

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

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

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

125
                if (m->manager->host_machine == m)
60✔
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);
60✔
130
        if (pidref_is_set(&m->leader)) {
60✔
131
                if (m->manager)
60✔
132
                        (void) hashmap_remove_value(m->manager->machines_by_leader, &m->leader, m);
60✔
133
                pidref_done(&m->leader);
60✔
134
        }
135

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

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

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

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

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

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

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

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

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

164
        assert(m);
93✔
165

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

255
        if (sl) {
93✔
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) {
65✔
268
        assert(m);
65✔
269

270
        if (m->unit && !m->subgroup) {
65✔
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)
65✔
276
                (void) unlink(m->state_file);
65✔
277
}
65✔
278

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

UNCOV
285
        int r;
×
286

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

UNCOV
290
        pidref_done(pidref);
×
291

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

UNCOV
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);
×
UNCOV
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;
×
UNCOV
315
        int r;
×
316

UNCOV
317
        assert(m);
×
318

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

UNCOV
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);
UNCOV
342
        if (r == -ENOENT)
×
343
                return 0;
344
        if (r < 0)
×
UNCOV
345
                return log_error_errno(r, "Failed to read %s: %m", m->state_file);
×
346

347
        if (!streq_ptr(name, m->name))
×
UNCOV
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)
×
UNCOV
351
                (void) sd_id128_from_string(id, &m->id);
×
352

353
        parse_pid_and_pidfdid(&m->leader, leader, leader_pidfdid, "leader");
×
UNCOV
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)
×
UNCOV
359
                        m->class = c;
×
360
        }
361

362
        if (realtime)
×
363
                (void) deserialize_usec(realtime, &m->timestamp.realtime);
×
364
        if (monotonic)
×
UNCOV
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;
×
UNCOV
371
                size_t nr = 0;
×
372

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

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

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

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

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

396
                m->netif = TAKE_PTR(ni);
×
UNCOV
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)
×
UNCOV
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)
×
UNCOV
410
                        log_warning_errno(r, "Failed to parse owning UID, ignoring: %s", uid);
×
411
        }
412

413
        return r;
414
}
415

UNCOV
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;
×
UNCOV
426
        int r;
×
427

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

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

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

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

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

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

457
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
×
UNCOV
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);
×
UNCOV
463
        if (r < 0)
×
464
                return r;
465

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

UNCOV
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));
UNCOV
476
        if (r < 0)
×
477
                return r;
478

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

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

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

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

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

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

506
        r = sd_bus_call(NULL, m, 0, &e, &reply);
×
UNCOV
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))
×
UNCOV
512
                        return machine_start_scope(machine, /* allow_pidfd = */ false, more_properties, error);
×
513

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

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

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

UNCOV
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) {
52✔
529
        int r;
52✔
530

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

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

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

542
        if (!m->subgroup) {
52✔
543
                r = hashmap_ensure_put(&m->manager->machines_by_unit, &string_hash_ops, m->unit, m);
28✔
544
                if (r < 0)
28✔
UNCOV
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) {
104✔
570
        int r;
104✔
571

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

578
        if (!pidref_is_set(pidref) || pidref->fd < 0)
206✔
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);
102✔
585
        if (r < 0)
102✔
586
                return r;
587

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

590
        return 0;
102✔
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(SYSTEMD_CGROUP_CONTROLLER, m->cgroup);
20✔
600
        if (r < 0)
20✔
UNCOV
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) {
52✔
610
        int r;
52✔
611

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

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

618
        _cleanup_free_ char *p = NULL;
24✔
619
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup, "cgroup.events", &p);
24✔
620
        if (r < 0)
24✔
UNCOV
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);
24✔
624
        if (r < 0)
24✔
UNCOV
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) {
60✔
631
        int r;
60✔
632

633
        assert(m);
60✔
634

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

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

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

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

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

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

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

662
        log_struct(LOG_INFO,
52✔
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))
52✔
669
                dual_timestamp_now(&m->timestamp);
52✔
670

671
        m->started = true;
52✔
672

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

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

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

680
        return 0;
52✔
681
}
682

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

686
        assert(m);
38✔
687

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

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

693
        if (m->unit && !m->subgroup) {
61✔
694
                /* If the machine runs as its own unit, then we'll terminate that */
UNCOV
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✔
UNCOV
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);
15✔
707
                if (r < 0)
15✔
708
                        return log_error_errno(r, "Failed to kill supervisor process " PID_FMT " of machine '%s': %m", m->supervisor.pid, m->name);
1✔
709
        } else
UNCOV
710
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Don't know how to terminate machine '%s'.", m->name);
×
711

712
        m->stopping = true;
37✔
713

714
        machine_save(m);
37✔
715

716
        return 0;
37✔
717
}
718

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

722
        if (m->started) {
65✔
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);
65✔
733
        machine_add_to_gc_queue(m);
65✔
734

735
        if (m->started) {
65✔
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;
65✔
743
}
744

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

748
        assert(m);
204✔
749

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

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

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

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

767
                r = manager_job_is_active(m->manager, m->scope_job, &error);
28✔
768
                if (r < 0)
28✔
UNCOV
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;
14✔
772
        }
773

774
        if (m->unit && !m->subgroup) {
80✔
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);
56✔
778
                if (r < 0)
56✔
UNCOV
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)
56✔
781
                        return false;
6✔
782
        }
783

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

792
        return true;
793
}
794

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

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

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

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

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

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

813
        if (s->stopping)
89✔
814
                return MACHINE_CLOSING;
815

816
        if (s->scope_job)
66✔
UNCOV
817
                return MACHINE_OPENING;
×
818

819
        return MACHINE_RUNNING;
820
}
821

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

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

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

830
        switch (whom) {
21✔
831

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

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

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

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

UNCOV
844
        default:
×
UNCOV
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:
UNCOV
858
                if (!pidref_is_set(&m->leader))
×
859
                        return -EINVAL;
860

UNCOV
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;
×
UNCOV
882
                char *address;
×
883

UNCOV
884
                r = sd_bus_new(&bus);
×
UNCOV
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

UNCOV
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);
×
UNCOV
899
                if (r < 0)
×
900
                        return r;
901

UNCOV
902
                *ret = TAKE_PTR(bus);
×
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
1127
                args[2] = NULL;
×
UNCOV
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_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
8✔
1145
        _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
8✔
UNCOV
1146
        _cleanup_(sigkill_waitp) pid_t child = 0;
×
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
1182
                return log_debug_errno(errno, "Failed to create pipe: %m");
×
1183

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

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

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

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

1242
                report_errno_and_exit(errno_pipe_fd[1], r);
8✔
1243
        }
1244

1245
        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
8✔
1246

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

1252
        TAKE_FD(errno_pipe_fd[0]);
8✔
1253
        TAKE_PID(child);
8✔
1254

1255
        *ret = operation;
8✔
1256
        return 0;
8✔
1257
}
1258

1259
void machine_release_unit(Machine *m) {
60✔
1260
        assert(m);
60✔
1261

1262
        if (!m->unit)
60✔
1263
                return;
1264

1265
        assert(m->manager);
60✔
1266

1267
        if (m->referenced) {
60✔
1268
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
UNCOV
1269
                int r;
×
1270

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

UNCOV
1277
                m->referenced = false;
×
1278
        }
1279

1280
        if (!m->subgroup)
60✔
1281
                (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
36✔
1282

1283
        m->unit = mfree(m->unit);
60✔
1284

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

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

1296
        assert(m);
46✔
1297
        assert(ret);
46✔
1298

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

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

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

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

UNCOV
1322
                return -errno;
×
1323
        }
1324

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

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

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

1344
        fclose(f);
41✔
1345

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

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

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

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

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

1379
        *ret = uid_shift;
41✔
1380
        return 0;
41✔
1381
}
1382

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

1389
        _cleanup_fclose_ FILE *f = NULL;
402✔
1390
        const char *p;
402✔
1391
        int r;
402✔
1392

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

1396
        assert(machine);
402✔
1397

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

1401
        if (machine->class != MACHINE_CONTAINER)
402✔
1402
                goto negative;
318✔
1403

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

1411
        for (;;) {
248✔
1412
                uid_t uid_base, uid_shift, uid_range, converted;
166✔
1413

1414
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
166✔
1415
                if (r == -ENOMSG)
166✔
1416
                        break;
1417
                if (r < 0)
84✔
1418
                        return r;
2✔
1419

1420
                /* The private user namespace is disabled, ignoring. */
1421
                if (uid_shift == 0)
84✔
1422
                        continue;
82✔
1423

1424
                if (uid < uid_shift || uid >= uid_shift + uid_range)
32✔
1425
                        continue;
30✔
1426

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

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

1435
                if (ret_internal_uid)
2✔
1436
                        *ret_internal_uid = converted;
2✔
1437

1438
                return true;
1439
        }
1440

1441
negative:
400✔
1442
        if (ret_internal_uid)
400✔
1443
                *ret_internal_uid = UID_INVALID;
400✔
1444

1445
        return false;
1446
}
1447

1448
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
198✔
1449
        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
198✔
1450
}
1451

1452
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
204✔
1453
        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
204✔
1454
}
1455

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

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

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

1469
        assert(machine);
4✔
1470
        assert(uid_is_valid(uid));
4✔
1471

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

1475
        /* Translates a machine UID into a host UID */
1476

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

1482
        for (;;) {
4✔
1483
                uid_t uid_base, uid_shift, uid_range, converted;
4✔
1484

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

1491
                if (uid < uid_base || uid >= uid_base + uid_range)
4✔
UNCOV
1492
                        continue;
×
1493

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

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

1502
                if (ret_host_uid)
4✔
1503
                        *ret_host_uid = converted;
4✔
1504

1505
                return 0;
1506
        }
1507

UNCOV
1508
        return -ESRCH;
×
1509
}
1510

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

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

1519
int machine_open_root_directory(Machine *machine) {
3✔
1520
        int r;
3✔
1521

1522
        assert(machine);
3✔
1523

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

1530
                return fd;
1531
        }
1532

1533
        case MACHINE_CONTAINER: {
1✔
1534
                _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
1✔
1535
                _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
2✔
1536
                pid_t child;
1✔
1537

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

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

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

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

1570
                        errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1✔
1571
                        fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
1✔
1572

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

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

1586
                        _exit(EXIT_SUCCESS);
1✔
1587
                }
1588

1589
                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1✔
1590
                fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
1✔
1591

1592
                r = wait_for_terminate_and_check("(sd-openrootns)", child, /* flags = */ 0);
1✔
1593
                if (r < 0)
1✔
UNCOV
1594
                        return log_debug_errno(r, "Failed to wait for child: %m");
×
1595

1596
                r = read_errno(errno_pipe_fd[0]); /* the function does debug reporting */
1✔
1597
                if (r < 0)
1✔
1598
                        return r;
1599

1600
                int fd = receive_one_fd(fd_pass_socket[0], MSG_DONTWAIT);
1✔
1601
                if (fd < 0)
1✔
UNCOV
1602
                        return log_debug_errno(fd, "Failed to receive FD from child: %m");
×
1603

1604
                return fd;
1605
        }
1606

1607
        default:
1608
                return -EOPNOTSUPP;
1609
        }
1610
}
1611

1612
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
1613
        [MACHINE_CONTAINER] = "container",
1614
        [MACHINE_VM]        = "vm",
1615
        [MACHINE_HOST]      = "host",
1616
};
1617

1618
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
338✔
1619

1620
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
1621
        [MACHINE_OPENING] = "opening",
1622
        [MACHINE_RUNNING] = "running",
1623
        [MACHINE_CLOSING] = "closing"
1624
};
1625

1626
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
87✔
1627

1628
static const char* const kill_whom_table[_KILL_WHOM_MAX] = {
1629
        [KILL_LEADER]     = "leader",
1630
        [KILL_SUPERVISOR] = "supervisor",
1631
        [KILL_ALL]        = "all",
1632
};
1633

1634
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
93✔
1635

1636
static const char* const acquire_metadata_table[_ACQUIRE_METADATA_MAX] = {
1637
        [ACQUIRE_METADATA_NO]       = "no",
1638
        [ACQUIRE_METADATA_YES]      = "yes",
1639
        [ACQUIRE_METADATA_GRACEFUL] = "graceful"
1640
};
1641

1642
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