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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

103
        machine->manager = manager;
77✔
104

105
        return 0;
77✔
106
}
107

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

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

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

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

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

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

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

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

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

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

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

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

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

154
        free(m->netif);
89✔
155
        free(m->ssh_address);
89✔
156
        free(m->ssh_private_key_path);
89✔
157
        free(m->control_address);
89✔
158

159
        return mfree(m);
89✔
160
}
161

162
int machine_save(Machine *m) {
101✔
163
        int r;
101✔
164

165
        assert(m);
101✔
166

167
        if (!m->state_file)
101✔
168
                return 0;
101✔
169

170
        if (!m->started)
101✔
171
                return 0;
172

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

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

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

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

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

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

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

207
        if (!sd_id128_is_null(m->id))
101✔
208
                fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
97✔
209

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

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

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

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

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

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

247
        env_file_fputs_assignment(f, "SSH_ADDRESS=", m->ssh_address);
101✔
248
        env_file_fputs_assignment(f, "SSH_PRIVATE_KEY_PATH=", m->ssh_private_key_path);
101✔
249
        env_file_fputs_assignment(f, "CONTROL_ADDRESS=", m->control_address);
101✔
250

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

255
        temp_path = mfree(temp_path); /* disarm auto-destroy: temporary file does not exist anymore */
101✔
256

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

262
                /* disarm auto-removal */
263
                sl = mfree(sl);
44✔
264
        }
265

266
        return 0;
267
}
268

269
static void machine_unlink(Machine *m) {
82✔
270
        assert(m);
82✔
271

272
        if (m->unit && !m->subgroup) {
82✔
273
                const char *sl = strjoina(m->manager->state_dir, "/unit:", m->unit);
224✔
274
                (void) unlink(sl);
32✔
275
        }
276

277
        if (m->state_file)
82✔
278
                (void) unlink(m->state_file);
82✔
279
}
82✔
280

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

287
        int r;
×
288

289
        assert(pidref);
×
290
        assert(name);
×
291

292
        pidref_done(pidref);
×
293

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

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

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

319
        assert(m);
×
320

321
        if (!m->state_file)
×
322
                return 0;
323

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

350
        if (!streq_ptr(name, m->name))
×
351
                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);
×
352

353
        if (id)
×
354
                (void) sd_id128_from_string(id, &m->id);
×
355

356
        parse_pid_and_pidfdid(&m->leader, leader, leader_pidfdid, "leader");
×
357
        parse_pid_and_pidfdid(&m->supervisor, supervisor, supervisor_pidfdid, "supervisor");
×
358

359
        if (class) {
×
360
                MachineClass c = machine_class_from_string(class);
×
361
                if (c >= 0)
×
362
                        m->class = c;
×
363
        }
364

365
        if (realtime)
×
366
                (void) deserialize_usec(realtime, &m->timestamp.realtime);
×
367
        if (monotonic)
×
368
                (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
×
369

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

376
                for (const char *p = netif;;) {
×
377
                        _cleanup_free_ char *word = NULL;
×
378

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

389
                        r = parse_ifindex(word);
×
390
                        if (r < 0)
×
391
                                continue;
×
392

393
                        if (!GREEDY_REALLOC(ni, nr + 1))
×
394
                                return log_oom();
×
395

396
                        ni[nr++] = r;
×
397
                }
398

399
                m->netif = TAKE_PTR(ni);
×
400
                m->n_netif = nr;
×
401
        }
402

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

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

416
        return r;
417
}
418

419
static int machine_start_scope(
×
420
                Machine *machine,
421
                bool allow_pidfd,
422
                sd_bus_message *more_properties,
423
                sd_bus_error *error) {
424

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

431
        assert(machine);
×
432
        assert(pidref_is_set(&machine->leader));
×
433
        assert(!machine->unit);
×
434
        assert(!machine->subgroup);
×
435

436
        escaped = unit_name_escape(machine->name);
×
437
        if (!escaped)
×
438
                return log_oom();
×
439

440
        unit = strjoin("machine-", escaped, ".scope");
×
441
        if (!unit)
×
442
                return log_oom();
×
443

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

452
        r = sd_bus_message_append(m, "ss", unit, "fail");
×
453
        if (r < 0)
×
454
                return r;
455

456
        r = sd_bus_message_open_container(m, 'a', "(sv)");
×
457
        if (r < 0)
×
458
                return r;
459

460
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
×
461
        if (r < 0)
×
462
                return r;
463

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

469
        r = bus_append_scope_pidref(m, &machine->leader, allow_pidfd);
×
470
        if (r < 0)
×
471
                return r;
472

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

482
        if (machine->uid != 0) {
×
483
                _cleanup_free_ char *u = NULL;
×
484

485
                if (asprintf(&u, UID_FMT, machine->uid) < 0)
×
486
                        return -ENOMEM;
487

488
                r = sd_bus_message_append(
×
489
                                m, "(sv)",
490
                                "User", "s", u);
491
                if (r < 0)
×
492
                        return r;
493
        }
494

495
        if (more_properties) {
×
496
                r = sd_bus_message_copy(m, more_properties, true);
×
497
                if (r < 0)
×
498
                        return r;
499
        }
500

501
        r = sd_bus_message_close_container(m);
×
502
        if (r < 0)
×
503
                return r;
504

505
        r = sd_bus_message_append(m, "a(sa(sv))", 0);
×
506
        if (r < 0)
×
507
                return r;
508

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

517
                return sd_bus_error_move(error, &e);
×
518
        }
519

520
        machine->unit = TAKE_PTR(unit);
×
521
        machine->referenced = true;
×
522

523
        const char *job;
×
524
        r = sd_bus_message_read(reply, "o", &job);
×
525
        if (r < 0)
×
526
                return r;
527

528
        return free_and_strdup(&machine->scope_job, job);
×
529
}
530

531
static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
66✔
532
        int r;
66✔
533

534
        assert(m);
66✔
535
        assert(m->class != MACHINE_HOST);
66✔
536

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

543
        assert(m->unit);
66✔
544

545
        if (!m->subgroup) {
66✔
546
                r = hashmap_ensure_put(&m->manager->machines_by_unit, &string_hash_ops, m->unit, m);
28✔
547
                if (r < 0)
28✔
548
                        return r;
×
549
        }
550

551
        return 0;
552
}
553

554
static int machine_dispatch_leader_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
61✔
555
        Machine *m = ASSERT_PTR(userdata);
61✔
556

557
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
61✔
558
        machine_add_to_gc_queue(m);
61✔
559

560
        return 0;
61✔
561
}
562

563
static int machine_dispatch_supervisor_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
3✔
564
        Machine *m = ASSERT_PTR(userdata);
3✔
565

566
        m->supervisor_pidfd_event_source = sd_event_source_disable_unref(m->supervisor_pidfd_event_source);
3✔
567
        machine_add_to_gc_queue(m);
3✔
568

569
        return 0;
3✔
570
}
571

572
static int machine_watch_pidfd(Machine *m, PidRef *pidref, sd_event_source **source, sd_event_io_handler_t cb) {
132✔
573
        int r;
132✔
574

575
        assert(m);
132✔
576
        assert(m->manager);
132✔
577
        assert(source);
132✔
578
        assert(!*source);
132✔
579
        assert(cb);
132✔
580

581
        if (!pidref_is_set(pidref) || pidref->fd < 0)
262✔
582
                return 0;
583

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

587
        r = sd_event_add_io(m->manager->event, source, pidref->fd, EPOLLIN, cb, m);
130✔
588
        if (r < 0)
130✔
589
                return r;
590

591
        (void) sd_event_source_set_description(*source, "machine-pidfd");
130✔
592

593
        return 0;
130✔
594
}
595

596
static int machine_dispatch_cgroup_empty(sd_event_source *s, const struct inotify_event *event, void *userdata) {
×
597
        Machine *m = ASSERT_PTR(userdata);
×
598
        int r;
×
599

600
        assert(m->cgroup);
×
601

602
        r = cg_is_empty(m->cgroup);
×
603
        if (r < 0)
×
604
                return log_error_errno(r, "Failed to determine if cgroup '%s' is empty: %m", m->cgroup);
×
605

606
        if (r > 0)
×
607
                machine_add_to_gc_queue(m);
×
608

609
        return 0;
610
}
611

612
static int machine_watch_cgroup(Machine *m) {
66✔
613
        int r;
66✔
614

615
        assert(m);
66✔
616
        assert(!m->cgroup_empty_event_source);
66✔
617

618
        if (!m->cgroup)
66✔
619
                return 0;
66✔
620

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

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

630
        return 0;
631
}
632

633
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
77✔
634
        int r;
77✔
635

636
        assert(m);
77✔
637

638
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
77✔
639
                return -EOPNOTSUPP;
640

641
        if (m->started)
66✔
642
                return 0;
643

644
        r = hashmap_ensure_put(&m->manager->machines_by_leader, &pidref_hash_ops, &m->leader, m);
66✔
645
        if (r < 0)
66✔
646
                return r;
647

648
        r = machine_watch_pidfd(m, &m->leader, &m->leader_pidfd_event_source, machine_dispatch_leader_pidfd);
66✔
649
        if (r < 0)
66✔
650
                return r;
651

652
        r = machine_watch_pidfd(m, &m->supervisor, &m->supervisor_pidfd_event_source, machine_dispatch_supervisor_pidfd);
66✔
653
        if (r < 0)
66✔
654
                return r;
655

656
        r = machine_watch_cgroup(m);
66✔
657
        if (r < 0)
66✔
658
                return r;
659

660
        /* Create cgroup */
661
        r = machine_ensure_scope(m, properties, error);
66✔
662
        if (r < 0)
66✔
663
                return r;
664

665
        log_struct(LOG_INFO,
66✔
666
                   LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START_STR),
667
                   LOG_ITEM("NAME=%s", m->name),
668
                   LOG_ITEM("LEADER="PID_FMT, m->leader.pid),
669
                   LOG_MESSAGE("New machine %s.", m->name));
670

671
        if (!dual_timestamp_is_set(&m->timestamp))
66✔
672
                dual_timestamp_now(&m->timestamp);
66✔
673

674
        m->started = true;
66✔
675

676
        /* Save new machine data */
677
        machine_save(m);
66✔
678

679
        machine_send_signal(m, "MachineNew");
66✔
680

681
        (void) manager_notify_hook_filters(m->manager);
66✔
682

683
        return 0;
66✔
684
}
685

686
int machine_stop(Machine *m) {
38✔
687
        int r;
38✔
688

689
        assert(m);
38✔
690

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

693
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
38✔
694
                return -EOPNOTSUPP;
695

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

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

705
                free_and_replace(m->scope_job, job);
16✔
706

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

715
        m->stopping = true;
35✔
716

717
        machine_save(m);
35✔
718

719
        return 0;
35✔
720
}
721

722
int machine_finalize(Machine *m) {
82✔
723
        assert(m);
82✔
724

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

732
                m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
61✔
733
        }
734

735
        machine_unlink(m);
82✔
736
        machine_add_to_gc_queue(m);
82✔
737

738
        if (m->started) {
82✔
739
                machine_send_signal(m, "MachineRemoved");
61✔
740
                m->started = false;
61✔
741

742
                (void) manager_notify_hook_filters(m->manager);
61✔
743
        }
744

745
        return 0;
82✔
746
}
747

748
bool machine_may_gc(Machine *m, bool drop_not_started) {
208✔
749
        int r;
208✔
750

751
        assert(m);
208✔
752

753
        if (m->class == MACHINE_HOST)
208✔
754
                return false;
755

756
        if (drop_not_started && !m->started)
164✔
757
                return true;
758

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

767
        if (m->scope_job) {
84✔
768
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
12✔
769

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

777
        if (m->unit && !m->subgroup) {
84✔
778
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
40✔
779

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

787
        if (m->cgroup) {
72✔
788
                r = cg_is_empty(m->cgroup);
2✔
789
                if (IN_SET(r, 0, -ENOENT))
2✔
790
                        return true;
791
                if (r < 0)
×
792
                        log_debug_errno(r, "Failed to determine if cgroup '%s' is empty, ignoring: %m", m->cgroup);
208✔
793
        }
794

795
        return true;
796
}
797

798
void machine_add_to_gc_queue(Machine *m) {
217✔
799
        assert(m);
217✔
800

801
        if (m->in_gc_queue)
217✔
802
                return;
803

804
        LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
165✔
805
        m->in_gc_queue = true;
165✔
806

807
        manager_enqueue_gc(m->manager);
165✔
808
}
809

810
MachineState machine_get_state(Machine *m) {
88✔
811
        assert(m);
88✔
812

813
        if (m->class == MACHINE_HOST)
88✔
814
                return MACHINE_RUNNING;
815

816
        if (m->stopping)
88✔
817
                return MACHINE_CLOSING;
818

819
        if (m->scope_job)
66✔
820
                return MACHINE_OPENING;
×
821

822
        return MACHINE_RUNNING;
823
}
824

825
int machine_kill(Machine *m, KillWhom whom, int signo) {
15✔
826
        assert(m);
15✔
827

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

830
        if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
15✔
831
                return -EOPNOTSUPP;
832

833
        switch (whom) {
15✔
834

835
        case KILL_LEADER:
14✔
836
                return pidref_kill(&m->leader, signo);
14✔
837

838
        case KILL_SUPERVISOR:
×
839
                return pidref_kill(&m->supervisor, signo);
×
840

841
        case KILL_ALL:
1✔
842
                if (!m->unit)
1✔
843
                        return -ESRCH;
844

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

847
        default:
×
848
                assert_not_reached();
×
849
        }
850
}
851

852
int machine_openpt(Machine *m, int flags, char **ret_peer) {
9✔
853
        assert(m);
9✔
854

855
        switch (m->class) {
9✔
856

857
        case MACHINE_HOST:
9✔
858
                return openpt_allocate(flags, ret_peer);
9✔
859

860
        case MACHINE_CONTAINER:
861
                if (!pidref_is_set(&m->leader))
×
862
                        return -EINVAL;
863

864
                return openpt_allocate_in_namespace(&m->leader, flags, ret_peer);
×
865

866
        default:
867
                return -EOPNOTSUPP;
868
        }
869
}
870

871
static int machine_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
7✔
872
        int r;
7✔
873

874
        assert(m);
7✔
875
        assert(ret);
7✔
876

877
        switch (m->class) {
7✔
878

879
        case MACHINE_HOST:
7✔
880
                *ret = NULL;
7✔
881
                return 0;
7✔
882

883
        case MACHINE_CONTAINER: {
×
884
                _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
×
885
                char *address;
×
886

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

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

894
                bus->address = address;
×
895
                bus->bus_client = true;
×
896
                bus->trusted = false;
×
897
                bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
×
898

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

905
                *ret = TAKE_PTR(bus);
×
906
                return 0;
×
907
        }
908

909
        default:
910
                return -EOPNOTSUPP;
911
        }
912
}
913

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

920
        assert(m);
1✔
921
        assert(ptmx_name);
1✔
922

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

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

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

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

938
        return 0;
939
}
940

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

957
        assert(m);
6✔
958
        assert(ptmx_fd >= 0);
6✔
959
        assert(ptmx_name);
6✔
960

961
        if (isempty(user) || isempty(path) || strv_isempty(args))
24✔
962
                return -EINVAL;
963

964
        p = path_startswith(ptmx_name, "/dev/pts/");
6✔
965
        utmp_id = path_startswith(ptmx_name, "/dev/");
6✔
966
        if (!p || !utmp_id)
6✔
967
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
968

969
        pty_fd = pty_open_peer(ptmx_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
6✔
970
        if (pty_fd < 0)
6✔
971
                return log_debug_errno(pty_fd, "Failed to open terminal: %m");
×
972

973
        r = machine_bus_new(m, error, &allocated_bus);
6✔
974
        if (r < 0)
6✔
975
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
976

977
        container_bus = allocated_bus ?: m->manager->system_bus;
6✔
978
        r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
6✔
979
        if (r < 0)
6✔
980
                return r;
981

982
        /* Name and mode */
983
        unit = strjoina("container-shell@", p, ".service");
42✔
984
        r = sd_bus_message_append(tm, "ss", unit, "fail");
6✔
985
        if (r < 0)
6✔
986
                return r;
987

988
        /* Properties */
989
        r = sd_bus_message_open_container(tm, 'a', "(sv)");
6✔
990
        if (r < 0)
6✔
991
                return r;
992

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

1012
        r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
6✔
1013
        if (r < 0)
6✔
1014
                return r;
1015

1016
        if (!strv_isempty(env)) {
6✔
1017
                r = sd_bus_message_open_container(tm, 'r', "sv");
5✔
1018
                if (r < 0)
5✔
1019
                        return r;
1020

1021
                r = sd_bus_message_append(tm, "s", "Environment");
5✔
1022
                if (r < 0)
5✔
1023
                        return r;
1024

1025
                r = sd_bus_message_open_container(tm, 'v', "as");
5✔
1026
                if (r < 0)
5✔
1027
                        return r;
1028

1029
                r = sd_bus_message_append_strv(tm, env);
5✔
1030
                if (r < 0)
5✔
1031
                        return r;
1032

1033
                r = sd_bus_message_close_container(tm);
5✔
1034
                if (r < 0)
5✔
1035
                        return r;
1036

1037
                r = sd_bus_message_close_container(tm);
5✔
1038
                if (r < 0)
5✔
1039
                        return r;
1040
        }
1041

1042
        /* Exec container */
1043
        r = sd_bus_message_open_container(tm, 'r', "sv");
6✔
1044
        if (r < 0)
6✔
1045
                return r;
1046

1047
        r = sd_bus_message_append(tm, "s", "ExecStart");
6✔
1048
        if (r < 0)
6✔
1049
                return r;
1050

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

1055
        r = sd_bus_message_open_container(tm, 'a', "(sasb)");
6✔
1056
        if (r < 0)
6✔
1057
                return r;
1058

1059
        r = sd_bus_message_open_container(tm, 'r', "sasb");
6✔
1060
        if (r < 0)
6✔
1061
                return r;
1062

1063
        r = sd_bus_message_append(tm, "s", path);
6✔
1064
        if (r < 0)
6✔
1065
                return r;
1066

1067
        r = sd_bus_message_append_strv(tm, args);
6✔
1068
        if (r < 0)
6✔
1069
                return r;
1070

1071
        r = sd_bus_message_append(tm, "b", true);
6✔
1072
        if (r < 0)
6✔
1073
                return r;
1074

1075
        r = sd_bus_message_close_container(tm);
6✔
1076
        if (r < 0)
6✔
1077
                return r;
1078

1079
        r = sd_bus_message_close_container(tm);
6✔
1080
        if (r < 0)
6✔
1081
                return r;
1082

1083
        r = sd_bus_message_close_container(tm);
6✔
1084
        if (r < 0)
6✔
1085
                return r;
1086

1087
        r = sd_bus_message_close_container(tm);
6✔
1088
        if (r < 0)
6✔
1089
                return r;
1090

1091
        r = sd_bus_message_close_container(tm);
6✔
1092
        if (r < 0)
6✔
1093
                return r;
1094

1095
        /* Auxiliary units */
1096
        r = sd_bus_message_append(tm, "a(sa(sv))", 0);
6✔
1097
        if (r < 0)
6✔
1098
                return r;
1099

1100
        r = sd_bus_call(container_bus, tm, 0, error, NULL);
6✔
1101
        if (r < 0)
6✔
1102
                return r;
×
1103

1104
        return 0;
1105
}
1106

1107
char** machine_default_shell_args(const char *user) {
1✔
1108
        _cleanup_strv_free_ char **args = NULL;
1✔
1109

1110
        assert(user);
1✔
1111

1112
        args = new0(char*, 5 + 1);
1✔
1113
        if (!args)
1✔
1114
                return NULL;
1115

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

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

1124
        args[2] = strdup(
1✔
1125
                     "shell=$(getent passwd \"$1\" 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"
1126
                     "exec \"${shell:-/bin/sh}\" -l"); /* -l means --login */
1127
        if (!args[2])
1✔
1128
                return NULL;
1129

1130
        args[3] = strdup("sh"); /* $0 placeholder for sh -c */
1✔
1131
        if (!args[3])
1✔
1132
                return NULL;
1133

1134
        args[4] = strdup(user); /* becomes $1 in the script */
1✔
1135
        if (!args[4])
1✔
1136
                return NULL;
1137

1138
        return TAKE_PTR(args);
1✔
1139
}
1140

1141
int machine_copy_from_to_operation(
8✔
1142
                Manager *manager,
1143
                Machine *machine,
1144
                const char *host_path,
1145
                const char *container_path,
1146
                bool copy_from_container,
1147
                CopyFlags copy_flags,
1148
                Operation **ret) {
1149

1150
        _cleanup_close_ int host_fd = -EBADF, target_mntns_fd = -EBADF, source_mntns_fd = -EBADF;
16✔
1151
        _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
×
1152
        _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
8✔
1153
        _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
8✔
1154
        uid_t uid_shift;
8✔
1155
        int r;
8✔
1156

1157
        assert(manager);
8✔
1158
        assert(machine);
8✔
1159
        assert(ret);
8✔
1160

1161
        if (isempty(host_path) || isempty(container_path))
24✔
1162
                return -EINVAL;
1163

1164
        r = path_extract_filename(host_path, &host_basename);
8✔
1165
        if (r < 0)
8✔
1166
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", host_path);
×
1167

1168
        r = path_extract_filename(container_path, &container_basename);
8✔
1169
        if (r < 0)
8✔
1170
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path);
×
1171

1172
        host_fd = open_parent(host_path, O_CLOEXEC, 0);
8✔
1173
        if (host_fd < 0)
8✔
1174
                return log_debug_errno(host_fd, "Failed to open host directory '%s': %m", host_path);
×
1175

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

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

1184
        source_mntns_fd = namespace_open_by_type(NAMESPACE_MOUNT);
8✔
1185
        if (source_mntns_fd < 0)
8✔
1186
                return log_debug_errno(source_mntns_fd, "Failed to open our own mount namespace: %m");
×
1187

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

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

1205
                _cleanup_close_ int container_fd = -EBADF;
×
1206
                container_fd = open_parent(container_path, O_CLOEXEC, 0);
8✔
1207
                if (container_fd < 0) {
8✔
1208
                        log_debug_errno(container_fd, "Failed to open container directory: %m");
×
1209
                        report_errno_and_exit(errno_pipe_fd[1], container_fd);
×
1210
                }
1211

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

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

1247
                report_errno_and_exit(errno_pipe_fd[1], r);
8✔
1248
        }
1249

1250
        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
8✔
1251

1252
        Operation *operation;
8✔
1253
        r = operation_new(manager, machine, &child, errno_pipe_fd[0], &operation);
8✔
1254
        if (r < 0)
8✔
1255
                return r;
1256

1257
        TAKE_FD(errno_pipe_fd[0]);
8✔
1258
        TAKE_PIDREF(child);
8✔
1259

1260
        *ret = operation;
8✔
1261
        return 0;
8✔
1262
}
1263

1264
void machine_release_unit(Machine *m) {
77✔
1265
        assert(m);
77✔
1266

1267
        if (!m->unit)
77✔
1268
                return;
1269

1270
        assert(m->manager);
77✔
1271

1272
        if (m->referenced) {
77✔
1273
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1274
                int r;
×
1275

1276
                r = manager_unref_unit(m->manager, m->unit, &error);
×
1277
                if (r < 0)
×
1278
                        log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
×
1279
                                       "Failed to drop reference to machine scope, ignoring: %s",
1280
                                       bus_error_message(&error, r));
1281

1282
                m->referenced = false;
×
1283
        }
1284

1285
        if (!m->subgroup)
77✔
1286
                (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
39✔
1287

1288
        m->unit = mfree(m->unit);
77✔
1289

1290
        /* Also free the subgroup, because it only makes sense in the context of the unit */
1291
        m->subgroup = mfree(m->subgroup);
77✔
1292
}
1293

1294
int machine_get_uid_shift(Machine *m, uid_t *ret) {
47✔
1295
        char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
47✔
1296
        uid_t uid_base, uid_shift, uid_range;
47✔
1297
        gid_t gid_base, gid_shift, gid_range;
47✔
1298
        _cleanup_fclose_ FILE *f = NULL;
47✔
1299
        int r;
47✔
1300

1301
        assert(m);
47✔
1302
        assert(ret);
47✔
1303

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

1310
        if (m->class == MACHINE_HOST) {
47✔
1311
                *ret = 0;
5✔
1312
                return 0;
5✔
1313
        }
1314

1315
        if (m->class != MACHINE_CONTAINER)
42✔
1316
                return -EOPNOTSUPP;
1317

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

1327
                return -errno;
×
1328
        }
1329

1330
        /* Read the first line. There's at least one. */
1331
        r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
42✔
1332
        if (r < 0)
42✔
1333
                return r;
1334

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

1342
        /* If there's more than one line, then we don't support this mapping. */
1343
        r = safe_fgetc(f, NULL);
42✔
1344
        if (r < 0)
42✔
1345
                return r;
1346
        if (r != 0) /* Insist on EOF */
42✔
1347
                return -ENXIO;
1348

1349
        fclose(f);
42✔
1350

1351
        xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
42✔
1352
        f = fopen(p, "re");
42✔
1353
        if (!f)
42✔
1354
                return -errno;
×
1355

1356
        /* Read the first line. There's at least one. */
1357
        errno = 0;
42✔
1358
        r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
42✔
1359
        if (r == EOF)
42✔
1360
                return errno_or_else(ENOMSG);
47✔
1361
        assert(r >= 0);
42✔
1362
        if (r != 3)
42✔
1363
                return -EBADMSG;
1364

1365
        /* If there's more than one line, then we don't support this file. */
1366
        r = safe_fgetc(f, NULL);
42✔
1367
        if (r < 0)
42✔
1368
                return r;
1369
        if (r != 0) /* Insist on EOF */
42✔
1370
                return -ENXIO;
1371

1372
        /* If the UID and GID mapping doesn't match, we don't support this mapping. */
1373
        if (uid_base != (uid_t) gid_base)
42✔
1374
                return -ENXIO;
1375
        if (uid_shift != (uid_t) gid_shift)
42✔
1376
                return -ENXIO;
1377
        if (uid_range != (uid_t) gid_range)
42✔
1378
                return -ENXIO;
1379

1380
        r = pidref_verify(&m->leader);
42✔
1381
        if (r < 0)
42✔
1382
                return r;
1383

1384
        *ret = uid_shift;
42✔
1385
        return 0;
42✔
1386
}
1387

1388
static int machine_owns_uid_internal(
414✔
1389
                Machine *machine,
1390
                const char *map_file, /* "uid_map" or "gid_map" */
1391
                uid_t uid,
1392
                uid_t *ret_internal_uid) {
1393

1394
        _cleanup_fclose_ FILE *f = NULL;
414✔
1395
        const char *p;
414✔
1396
        int r;
414✔
1397

1398
        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
1399
        assert_cc(sizeof(uid_t) == sizeof(gid_t));
414✔
1400

1401
        assert(machine);
414✔
1402

1403
        /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
1404
         * internally in the machine */
1405

1406
        if (machine->class != MACHINE_CONTAINER)
414✔
1407
                goto negative;
338✔
1408

1409
        p = procfs_file_alloca(machine->leader.pid, map_file);
76✔
1410
        f = fopen(p, "re");
76✔
1411
        if (!f) {
76✔
1412
                log_debug_errno(errno, "Failed to open %s, ignoring.", p);
×
1413
                goto negative;
×
1414
        }
1415

1416
        for (;;) {
224✔
1417
                uid_t uid_base, uid_shift, uid_range, converted;
150✔
1418

1419
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
150✔
1420
                if (r == -ENOMSG)
150✔
1421
                        break;
1422
                if (r < 0)
76✔
1423
                        return r;
2✔
1424

1425
                /* The private user namespace is disabled, ignoring. */
1426
                if (uid_shift == 0)
76✔
1427
                        continue;
74✔
1428

1429
                if (uid < uid_shift || uid >= uid_shift + uid_range)
20✔
1430
                        continue;
18✔
1431

1432
                converted = (uid - uid_shift + uid_base);
2✔
1433
                if (!uid_is_valid(converted))
2✔
1434
                        return -EINVAL;
1435

1436
                r = pidref_verify(&machine->leader);
2✔
1437
                if (r < 0)
2✔
1438
                        return r;
1439

1440
                if (ret_internal_uid)
2✔
1441
                        *ret_internal_uid = converted;
2✔
1442

1443
                return true;
1444
        }
1445

1446
negative:
412✔
1447
        if (ret_internal_uid)
412✔
1448
                *ret_internal_uid = UID_INVALID;
412✔
1449

1450
        return false;
1451
}
1452

1453
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
204✔
1454
        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
204✔
1455
}
1456

1457
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
210✔
1458
        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
210✔
1459
}
1460

1461
static int machine_translate_uid_internal(
4✔
1462
                Machine *machine,
1463
                const char *map_file, /* "uid_map" or "gid_map" */
1464
                uid_t uid,
1465
                uid_t *ret_host_uid) {
1466

1467
        _cleanup_fclose_ FILE *f = NULL;
4✔
1468
        const char *p;
4✔
1469
        int r;
4✔
1470

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

1474
        assert(machine);
4✔
1475
        assert(uid_is_valid(uid));
4✔
1476

1477
        if (machine->class != MACHINE_CONTAINER)
4✔
1478
                return -ESRCH;
1479

1480
        /* Translates a machine UID into a host UID */
1481

1482
        p = procfs_file_alloca(machine->leader.pid, map_file);
4✔
1483
        f = fopen(p, "re");
4✔
1484
        if (!f)
4✔
1485
                return -errno;
×
1486

1487
        for (;;) {
4✔
1488
                uid_t uid_base, uid_shift, uid_range, converted;
4✔
1489

1490
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
4✔
1491
                if (r == -ENOMSG)
4✔
1492
                        break;
1493
                if (r < 0)
4✔
1494
                        return r;
4✔
1495

1496
                if (uid < uid_base || uid >= uid_base + uid_range)
4✔
1497
                        continue;
×
1498

1499
                converted = uid - uid_base + uid_shift;
4✔
1500
                if (!uid_is_valid(converted))
4✔
1501
                        return -EINVAL;
1502

1503
                r = pidref_verify(&machine->leader);
4✔
1504
                if (r < 0)
4✔
1505
                        return r;
1506

1507
                if (ret_host_uid)
4✔
1508
                        *ret_host_uid = converted;
4✔
1509

1510
                return 0;
1511
        }
1512

1513
        return -ESRCH;
×
1514
}
1515

1516
int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
2✔
1517
        return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
2✔
1518
}
1519

1520
int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
2✔
1521
        return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
2✔
1522
}
1523

1524
int machine_open_root_directory(Machine *machine) {
3✔
1525
        int r;
3✔
1526

1527
        assert(machine);
3✔
1528

1529
        switch (machine->class) {
3✔
1530
        case MACHINE_HOST: {
2✔
1531
                int fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
2✔
1532
                if (fd < 0)
2✔
1533
                        return log_debug_errno(errno, "Failed to open host root directory: %m");
×
1534

1535
                return fd;
1536
        }
1537

1538
        case MACHINE_CONTAINER: {
1✔
1539
                _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
1✔
1540
                _cleanup_(pidref_done) PidRef child = PIDREF_NULL;
×
1541
                _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
2✔
1542

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

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

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

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

1573
                        errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1✔
1574
                        fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
1✔
1575

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

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

1589
                        _exit(EXIT_SUCCESS);
1✔
1590
                }
1591

1592
                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1✔
1593
                fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
1✔
1594

1595
                r = pidref_wait_for_terminate_and_check("(sd-openrootns)", &child, /* flags= */ 0);
1✔
1596
                if (r < 0)
1✔
1597
                        return log_debug_errno(r, "Failed to wait for child: %m");
×
1598

1599
                r = read_errno(errno_pipe_fd[0]); /* the function does debug reporting */
1✔
1600
                if (r < 0)
1✔
1601
                        return r;
1602

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

1607
                return fd;
1608
        }
1609

1610
        default:
1611
                return -EOPNOTSUPP;
1612
        }
1613
}
1614

1615
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
1616
        [MACHINE_CONTAINER] = "container",
1617
        [MACHINE_VM]        = "vm",
1618
        [MACHINE_HOST]      = "host",
1619
};
1620

1621
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
413✔
1622

1623
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
1624
        [MACHINE_OPENING] = "opening",
1625
        [MACHINE_RUNNING] = "running",
1626
        [MACHINE_CLOSING] = "closing"
1627
};
1628

1629
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
87✔
1630

1631
static const char* const kill_whom_table[_KILL_WHOM_MAX] = {
1632
        [KILL_LEADER]     = "leader",
1633
        [KILL_SUPERVISOR] = "supervisor",
1634
        [KILL_ALL]        = "all",
1635
};
1636

1637
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
88✔
1638

1639
static const char* const acquire_metadata_table[_ACQUIRE_METADATA_MAX] = {
1640
        [ACQUIRE_METADATA_NO]       = "no",
1641
        [ACQUIRE_METADATA_YES]      = "yes",
1642
        [ACQUIRE_METADATA_GRACEFUL] = "graceful"
1643
};
1644

1645
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(acquire_metadata, AcquireMetadata, ACQUIRE_METADATA_YES);
11✔
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