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

systemd / systemd / 16280725298

14 Jul 2025 08:16PM UTC coverage: 72.166% (-0.006%) from 72.172%
16280725298

push

github

web-flow
Two fixlets for coverage test (#38183)

302135 of 418667 relevant lines covered (72.17%)

773261.64 hits per line

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

77.56
/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 "escape.h"
19
#include "extract-word.h"
20
#include "fd-util.h"
21
#include "fileio.h"
22
#include "format-util.h"
23
#include "fs-util.h"
24
#include "hashmap.h"
25
#include "log.h"
26
#include "machine.h"
27
#include "machine-dbus.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) {
109✔
50
        _cleanup_(machine_freep) Machine *m = NULL;
109✔
51

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

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

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

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

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

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

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

91
        if (machine->class != MACHINE_HOST) {
109✔
92
                char *temp = path_join("/run/systemd/machines", machine->name);
102✔
93
                if (!temp)
102✔
94
                        return -ENOMEM;
109✔
95

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

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

103
        machine->manager = manager;
109✔
104

105
        return 0;
109✔
106
}
107

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

164
        assert(m);
343✔
165

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

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

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

179
        r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
341✔
180
        if (r < 0)
341✔
181
                return log_error_errno(r, "Failed to create /run/systemd/machines/: %m");
×
182

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

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

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

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

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

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

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

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

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

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

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

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

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

264
        return 0;
265
}
266

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

270
        if (m->unit && !m->subgroup) {
135✔
271
                const char *sl = strjoina("/run/systemd/machines/unit:", m->unit);
560✔
272
                (void) unlink(sl);
112✔
273
        }
274

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

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

285
        int r;
×
286

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

290
        pidref_done(pidref);
×
291

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

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

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

317
        assert(m);
×
318

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

411
        return r;
412
}
413

414
static int machine_start_scope(
82✔
415
                Machine *machine,
416
                bool allow_pidfd,
417
                sd_bus_message *more_properties,
418
                sd_bus_error *error) {
419

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

426
        assert(machine);
82✔
427
        assert(pidref_is_set(&machine->leader));
82✔
428
        assert(!machine->unit);
82✔
429
        assert(!machine->subgroup);
82✔
430

431
        escaped = unit_name_escape(machine->name);
82✔
432
        if (!escaped)
82✔
433
                return log_oom();
×
434

435
        unit = strjoin("machine-", escaped, ".scope");
82✔
436
        if (!unit)
82✔
437
                return log_oom();
×
438

439
        r = bus_message_new_method_call(
164✔
440
                        machine->manager->bus,
82✔
441
                        &m,
442
                        bus_systemd_mgr,
443
                        "StartTransientUnit");
444
        if (r < 0)
82✔
445
                return r;
446

447
        r = sd_bus_message_append(m, "ss", unit, "fail");
82✔
448
        if (r < 0)
82✔
449
                return r;
450

451
        r = sd_bus_message_open_container(m, 'a', "(sv)");
82✔
452
        if (r < 0)
82✔
453
                return r;
454

455
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
82✔
456
        if (r < 0)
82✔
457
                return r;
458

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

464
        r = bus_append_scope_pidref(m, &machine->leader, allow_pidfd);
82✔
465
        if (r < 0)
82✔
466
                return r;
467

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

477
        if (machine->uid != 0) {
82✔
478
                _cleanup_free_ char *u = NULL;
×
479

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

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

490
        if (more_properties) {
82✔
491
                r = sd_bus_message_copy(m, more_properties, true);
82✔
492
                if (r < 0)
82✔
493
                        return r;
494
        }
495

496
        r = sd_bus_message_close_container(m);
82✔
497
        if (r < 0)
82✔
498
                return r;
499

500
        r = sd_bus_message_append(m, "a(sa(sv))", 0);
82✔
501
        if (r < 0)
82✔
502
                return r;
503

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

512
                return sd_bus_error_move(error, &e);
×
513
        }
514

515
        machine->unit = TAKE_PTR(unit);
82✔
516
        machine->referenced = true;
82✔
517

518
        const char *job;
82✔
519
        r = sd_bus_message_read(reply, "o", &job);
82✔
520
        if (r < 0)
82✔
521
                return r;
522

523
        return free_and_strdup(&machine->scope_job, job);
82✔
524
}
525

526
static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
102✔
527
        int r;
102✔
528

529
        assert(m);
102✔
530
        assert(m->class != MACHINE_HOST);
102✔
531

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

538
        assert(m->unit);
102✔
539

540
        if (!m->subgroup) {
102✔
541
                r = hashmap_ensure_put(&m->manager->machines_by_unit, &string_hash_ops, m->unit, m);
84✔
542
                if (r < 0)
84✔
543
                        return r;
×
544
        }
545

546
        return 0;
547
}
548

549
static int machine_dispatch_leader_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
101✔
550
        Machine *m = ASSERT_PTR(userdata);
101✔
551

552
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
101✔
553
        machine_add_to_gc_queue(m);
101✔
554

555
        return 0;
101✔
556
}
557

558
static int machine_dispatch_supervisor_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
×
559
        Machine *m = ASSERT_PTR(userdata);
×
560

561
        m->supervisor_pidfd_event_source = sd_event_source_disable_unref(m->supervisor_pidfd_event_source);
×
562
        machine_add_to_gc_queue(m);
×
563

564
        return 0;
×
565
}
566

567
static int machine_watch_pidfd(Machine *m, PidRef *pidref, sd_event_source **source, sd_event_io_handler_t cb) {
204✔
568
        int r;
204✔
569

570
        assert(m);
204✔
571
        assert(m->manager);
204✔
572
        assert(source);
204✔
573
        assert(!*source);
204✔
574
        assert(cb);
204✔
575

576
        if (!pidref_is_set(pidref) || pidref->fd < 0)
406✔
577
                return 0;
578

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

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

586
        (void) sd_event_source_set_description(*source, "machine-pidfd");
202✔
587

588
        return 0;
202✔
589
}
590

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

595
        assert(m->cgroup);
14✔
596

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

601
        if (r > 0)
14✔
602
                machine_add_to_gc_queue(m);
14✔
603

604
        return 0;
605
}
606

607
static int machine_watch_cgroup(Machine *m) {
102✔
608
        int r;
102✔
609

610
        assert(m);
102✔
611
        assert(!m->cgroup_empty_event_source);
102✔
612

613
        if (!m->cgroup)
102✔
614
                return 0;
102✔
615

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

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

625
        return 0;
626
}
627

628
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
109✔
629
        int r;
109✔
630

631
        assert(m);
109✔
632

633
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
109✔
634
                return -EOPNOTSUPP;
635

636
        if (m->started)
102✔
637
                return 0;
638

639
        r = hashmap_ensure_put(&m->manager->machines_by_leader, &pidref_hash_ops, &m->leader, m);
102✔
640
        if (r < 0)
102✔
641
                return r;
642

643
        r = machine_watch_pidfd(m, &m->leader, &m->leader_pidfd_event_source, machine_dispatch_leader_pidfd);
102✔
644
        if (r < 0)
102✔
645
                return r;
646

647
        r = machine_watch_pidfd(m, &m->supervisor, &m->supervisor_pidfd_event_source, machine_dispatch_supervisor_pidfd);
102✔
648
        if (r < 0)
102✔
649
                return r;
650

651
        r = machine_watch_cgroup(m);
102✔
652
        if (r < 0)
102✔
653
                return r;
654

655
        /* Create cgroup */
656
        r = machine_ensure_scope(m, properties, error);
102✔
657
        if (r < 0)
102✔
658
                return r;
659

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

666
        if (!dual_timestamp_is_set(&m->timestamp))
102✔
667
                dual_timestamp_now(&m->timestamp);
102✔
668

669
        m->started = true;
102✔
670

671
        /* Save new machine data */
672
        machine_save(m);
102✔
673

674
        machine_send_signal(m, true);
102✔
675

676
        return 0;
102✔
677
}
678

679
int machine_stop(Machine *m) {
75✔
680
        int r;
75✔
681

682
        assert(m);
75✔
683

684
        log_debug("Stopping machine '%s'.", m->name);
75✔
685

686
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
75✔
687
                return -EOPNOTSUPP;
688

689
        if (m->unit && !m->subgroup) {
136✔
690
                /* If the machine runs as its own unit, then we'll terminate that */
691
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
692
                char *job = NULL;
61✔
693

694
                r = manager_stop_unit(m->manager, m->unit, &error, &job);
61✔
695
                if (r < 0)
61✔
696
                        return log_error_errno(r, "Failed to stop machine unit: %s", bus_error_message(&error, r));
×
697

698
                free_and_replace(m->scope_job, job);
61✔
699

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

708
        m->stopping = true;
75✔
709

710
        machine_save(m);
75✔
711

712
        return 0;
75✔
713
}
714

715
int machine_finalize(Machine *m) {
135✔
716
        assert(m);
135✔
717

718
        if (m->started) {
135✔
719
                log_struct(LOG_INFO,
101✔
720
                           LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP_STR),
721
                           LOG_ITEM("NAME=%s", m->name),
722
                           LOG_ITEM("LEADER="PID_FMT, m->leader.pid),
723
                           LOG_MESSAGE("Machine %s terminated.", m->name));
724

725
                m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
101✔
726
        }
727

728
        machine_unlink(m);
135✔
729
        machine_add_to_gc_queue(m);
135✔
730

731
        if (m->started) {
135✔
732
                machine_send_signal(m, false);
101✔
733
                m->started = false;
101✔
734
        }
735

736
        return 0;
135✔
737
}
738

739
bool machine_may_gc(Machine *m, bool drop_not_started) {
618✔
740
        int r;
618✔
741

742
        assert(m);
618✔
743

744
        if (m->class == MACHINE_HOST)
618✔
745
                return false;
746

747
        if (drop_not_started && !m->started)
598✔
748
                return true;
749

750
        r = pidref_is_alive(&m->leader);
530✔
751
        if (r == -ESRCH)
530✔
752
                return true;
753
        if (r < 0)
530✔
754
                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", m->leader.pid);
×
755
        if (r > 0)
530✔
756
                return false;
757

758
        if (m->scope_job) {
154✔
759
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
55✔
760

761
                r = manager_job_is_active(m->manager, m->scope_job, &error);
61✔
762
                if (r < 0)
61✔
763
                        log_debug_errno(r, "Failed to determine whether job '%s' is active, assuming it is: %s", m->scope_job, bus_error_message(&error, r));
×
764
                if (r != 0)
61✔
765
                        return false;
6✔
766
        }
767

768
        if (m->unit && !m->subgroup) {
148✔
769
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
117✔
770

771
                r = manager_unit_is_active(m->manager, m->unit, &error);
126✔
772
                if (r < 0)
126✔
773
                        log_debug_errno(r, "Failed to determine whether unit '%s' is active, assuming it is: %s", m->unit, bus_error_message(&error, r));
×
774
                if (r != 0)
126✔
775
                        return false;
9✔
776
        }
777

778
        if (m->cgroup) {
139✔
779
                r = cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, m->cgroup);
22✔
780
                if (IN_SET(r, 0, -ENOENT))
22✔
781
                        return true;
782
                if (r < 0)
22✔
783
                        log_debug_errno(r, "Failed to determine if cgroup '%s' is empty, ignoring: %m", m->cgroup);
618✔
784
        }
785

786
        return true;
787
}
788

789
void machine_add_to_gc_queue(Machine *m) {
906✔
790
        assert(m);
906✔
791

792
        if (m->in_gc_queue)
906✔
793
                return;
794

795
        LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
410✔
796
        m->in_gc_queue = true;
410✔
797

798
        manager_enqueue_gc(m->manager);
410✔
799
}
800

801
MachineState machine_get_state(Machine *s) {
141✔
802
        assert(s);
141✔
803

804
        if (s->class == MACHINE_HOST)
141✔
805
                return MACHINE_RUNNING;
806

807
        if (s->stopping)
141✔
808
                return MACHINE_CLOSING;
809

810
        if (s->scope_job)
104✔
811
                return MACHINE_OPENING;
×
812

813
        return MACHINE_RUNNING;
814
}
815

816
int machine_kill(Machine *m, KillWhom whom, int signo) {
21✔
817
        assert(m);
21✔
818

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

821
        if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
21✔
822
                return -EOPNOTSUPP;
823

824
        switch (whom) {
21✔
825

826
        case KILL_LEADER:
17✔
827
                return pidref_kill(&m->leader, signo);
17✔
828

829
        case KILL_SUPERVISOR:
×
830
                return pidref_kill(&m->supervisor, signo);
×
831

832
        case KILL_ALL:
4✔
833
                if (!m->unit)
4✔
834
                        return -ESRCH;
835

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

838
        default:
×
839
                assert_not_reached();
×
840
        }
841
}
842

843
int machine_openpt(Machine *m, int flags, char **ret_peer) {
8✔
844
        assert(m);
8✔
845

846
        switch (m->class) {
8✔
847

848
        case MACHINE_HOST:
8✔
849
                return openpt_allocate(flags, ret_peer);
8✔
850

851
        case MACHINE_CONTAINER:
852
                if (!pidref_is_set(&m->leader))
×
853
                        return -EINVAL;
854

855
                return openpt_allocate_in_namespace(&m->leader, flags, ret_peer);
×
856

857
        default:
858
                return -EOPNOTSUPP;
859
        }
860
}
861

862
static int machine_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
6✔
863
        int r;
6✔
864

865
        assert(m);
6✔
866
        assert(ret);
6✔
867

868
        switch (m->class) {
6✔
869

870
        case MACHINE_HOST:
6✔
871
                *ret = NULL;
6✔
872
                return 0;
6✔
873

874
        case MACHINE_CONTAINER: {
×
875
                _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
×
876
                char *address;
×
877

878
                r = sd_bus_new(&bus);
×
879
                if (r < 0)
×
880
                        return log_debug_errno(r, "Failed to allocate new DBus: %m");
×
881

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

885
                bus->address = address;
×
886
                bus->bus_client = true;
×
887
                bus->trusted = false;
×
888
                bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
×
889

890
                r = sd_bus_start(bus);
×
891
                if (r == -ENOENT)
×
892
                        return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
×
893
                if (r < 0)
×
894
                        return r;
895

896
                *ret = TAKE_PTR(bus);
×
897
                return 0;
×
898
        }
899

900
        default:
901
                return -EOPNOTSUPP;
902
        }
903
}
904

905
int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error) {
1✔
906
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
1✔
907
        sd_bus *container_bus = NULL;
1✔
908
        const char *p, *getty;
1✔
909
        int r;
1✔
910

911
        assert(m);
1✔
912
        assert(ptmx_name);
1✔
913

914
        p = path_startswith(ptmx_name, "/dev/pts/");
1✔
915
        if (!p)
1✔
916
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
917

918
        r = machine_bus_new(m, error, &allocated_bus);
1✔
919
        if (r < 0)
1✔
920
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
921

922
        container_bus = allocated_bus ?: m->manager->bus;
1✔
923
        getty = strjoina("container-getty@", p, ".service");
7✔
924

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

929
        return 0;
930
}
931

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

948
        assert(m);
5✔
949
        assert(ptmx_fd >= 0);
5✔
950
        assert(ptmx_name);
5✔
951

952
        if (isempty(user) || isempty(path) || strv_isempty(args))
20✔
953
                return -EINVAL;
954

955
        p = path_startswith(ptmx_name, "/dev/pts/");
5✔
956
        utmp_id = path_startswith(ptmx_name, "/dev/");
5✔
957
        if (!p || !utmp_id)
5✔
958
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
959

960
        pty_fd = pty_open_peer(ptmx_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
5✔
961
        if (pty_fd < 0)
5✔
962
                return log_debug_errno(pty_fd, "Failed to open terminal: %m");
×
963

964
        r = machine_bus_new(m, error, &allocated_bus);
5✔
965
        if (r < 0)
5✔
966
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
967

968
        container_bus = allocated_bus ?: m->manager->bus;
5✔
969
        r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
5✔
970
        if (r < 0)
5✔
971
                return r;
972

973
        /* Name and mode */
974
        unit = strjoina("container-shell@", p, ".service");
35✔
975
        r = sd_bus_message_append(tm, "ss", unit, "fail");
5✔
976
        if (r < 0)
5✔
977
                return r;
978

979
        /* Properties */
980
        r = sd_bus_message_open_container(tm, 'a', "(sv)");
5✔
981
        if (r < 0)
5✔
982
                return r;
983

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

1003
        r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
5✔
1004
        if (r < 0)
5✔
1005
                return r;
1006

1007
        if (!strv_isempty(env)) {
5✔
1008
                r = sd_bus_message_open_container(tm, 'r', "sv");
4✔
1009
                if (r < 0)
4✔
1010
                        return r;
1011

1012
                r = sd_bus_message_append(tm, "s", "Environment");
4✔
1013
                if (r < 0)
4✔
1014
                        return r;
1015

1016
                r = sd_bus_message_open_container(tm, 'v', "as");
4✔
1017
                if (r < 0)
4✔
1018
                        return r;
1019

1020
                r = sd_bus_message_append_strv(tm, env);
4✔
1021
                if (r < 0)
4✔
1022
                        return r;
1023

1024
                r = sd_bus_message_close_container(tm);
4✔
1025
                if (r < 0)
4✔
1026
                        return r;
1027

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

1033
        /* Exec container */
1034
        r = sd_bus_message_open_container(tm, 'r', "sv");
5✔
1035
        if (r < 0)
5✔
1036
                return r;
1037

1038
        r = sd_bus_message_append(tm, "s", "ExecStart");
5✔
1039
        if (r < 0)
5✔
1040
                return r;
1041

1042
        r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
5✔
1043
        if (r < 0)
5✔
1044
                return r;
1045

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

1050
        r = sd_bus_message_open_container(tm, 'r', "sasb");
5✔
1051
        if (r < 0)
5✔
1052
                return r;
1053

1054
        r = sd_bus_message_append(tm, "s", path);
5✔
1055
        if (r < 0)
5✔
1056
                return r;
1057

1058
        r = sd_bus_message_append_strv(tm, args);
5✔
1059
        if (r < 0)
5✔
1060
                return r;
1061

1062
        r = sd_bus_message_append(tm, "b", true);
5✔
1063
        if (r < 0)
5✔
1064
                return r;
1065

1066
        r = sd_bus_message_close_container(tm);
5✔
1067
        if (r < 0)
5✔
1068
                return r;
1069

1070
        r = sd_bus_message_close_container(tm);
5✔
1071
        if (r < 0)
5✔
1072
                return r;
1073

1074
        r = sd_bus_message_close_container(tm);
5✔
1075
        if (r < 0)
5✔
1076
                return r;
1077

1078
        r = sd_bus_message_close_container(tm);
5✔
1079
        if (r < 0)
5✔
1080
                return r;
1081

1082
        r = sd_bus_message_close_container(tm);
5✔
1083
        if (r < 0)
5✔
1084
                return r;
1085

1086
        /* Auxiliary units */
1087
        r = sd_bus_message_append(tm, "a(sa(sv))", 0);
5✔
1088
        if (r < 0)
5✔
1089
                return r;
1090

1091
        r = sd_bus_call(container_bus, tm, 0, error, NULL);
5✔
1092
        if (r < 0)
5✔
1093
                return r;
×
1094

1095
        return 0;
1096
}
1097

1098
char** machine_default_shell_args(const char *user) {
1✔
1099
        _cleanup_strv_free_ char **args = NULL;
1✔
1100
        int r;
1✔
1101

1102
        assert(user);
1✔
1103

1104
        args = new0(char*, 3 + 1);
1✔
1105
        if (!args)
1✔
1106
                return NULL;
1107

1108
        args[0] = strdup("sh");
1✔
1109
        if (!args[0])
1✔
1110
                return NULL;
1111

1112
        args[1] = strdup("-c");
1✔
1113
        if (!args[1])
1✔
1114
                return NULL;
1115

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

1125
        return TAKE_PTR(args);
1✔
1126
}
1127

1128
int machine_copy_from_to_operation(
8✔
1129
                Manager *manager,
1130
                Machine *machine,
1131
                const char *host_path,
1132
                const char *container_path,
1133
                bool copy_from_container,
1134
                CopyFlags copy_flags,
1135
                Operation **ret) {
1136

1137
        _cleanup_close_ int host_fd = -EBADF, target_mntns_fd = -EBADF, source_mntns_fd = -EBADF;
16✔
1138
        _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
8✔
1139
        _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
8✔
1140
        _cleanup_(sigkill_waitp) pid_t child = 0;
×
1141
        uid_t uid_shift;
8✔
1142
        int r;
8✔
1143

1144
        assert(manager);
8✔
1145
        assert(machine);
8✔
1146
        assert(ret);
8✔
1147

1148
        if (isempty(host_path) || isempty(container_path))
24✔
1149
                return -EINVAL;
1150

1151
        r = path_extract_filename(host_path, &host_basename);
8✔
1152
        if (r < 0)
8✔
1153
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", host_path);
×
1154

1155
        r = path_extract_filename(container_path, &container_basename);
8✔
1156
        if (r < 0)
8✔
1157
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path);
×
1158

1159
        host_fd = open_parent(host_path, O_CLOEXEC, 0);
8✔
1160
        if (host_fd < 0)
8✔
1161
                return log_debug_errno(host_fd, "Failed to open host directory '%s': %m", host_path);
×
1162

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

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

1171
        source_mntns_fd = namespace_open_by_type(NAMESPACE_MOUNT);
8✔
1172
        if (source_mntns_fd < 0)
8✔
1173
                return log_debug_errno(source_mntns_fd, "Failed to open our own mount namespace: %m");
×
1174

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

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

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

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

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

1236
                report_errno_and_exit(errno_pipe_fd[1], r);
8✔
1237
        }
1238

1239
        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
8✔
1240

1241
        Operation *operation;
8✔
1242
        r = operation_new(manager, machine, child, errno_pipe_fd[0], &operation);
8✔
1243
        if (r < 0)
8✔
1244
                return r;
1245

1246
        TAKE_FD(errno_pipe_fd[0]);
8✔
1247
        TAKE_PID(child);
8✔
1248

1249
        *ret = operation;
8✔
1250
        return 0;
8✔
1251
}
1252

1253
void machine_release_unit(Machine *m) {
109✔
1254
        assert(m);
109✔
1255

1256
        if (!m->unit)
109✔
1257
                return;
1258

1259
        assert(m->manager);
109✔
1260

1261
        if (m->referenced) {
109✔
1262
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1263
                int r;
82✔
1264

1265
                r = manager_unref_unit(m->manager, m->unit, &error);
82✔
1266
                if (r < 0)
82✔
1267
                        log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
×
1268
                                       "Failed to drop reference to machine scope, ignoring: %s",
1269
                                       bus_error_message(&error, r));
1270

1271
                m->referenced = false;
82✔
1272
        }
1273

1274
        if (!m->subgroup)
109✔
1275
                (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
91✔
1276

1277
        m->unit = mfree(m->unit);
109✔
1278

1279
        /* Also free the subgroup, because it only makes sense in the context of the unit */
1280
        m->subgroup = mfree(m->subgroup);
109✔
1281
}
1282

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

1290
        assert(m);
47✔
1291
        assert(ret);
47✔
1292

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

1299
        if (m->class == MACHINE_HOST) {
47✔
1300
                *ret = 0;
5✔
1301
                return 0;
5✔
1302
        }
1303

1304
        if (m->class != MACHINE_CONTAINER)
42✔
1305
                return -EOPNOTSUPP;
1306

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

1316
                return -errno;
×
1317
        }
1318

1319
        /* Read the first line. There's at least one. */
1320
        r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
42✔
1321
        if (r < 0)
42✔
1322
                return r;
1323

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

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

1338
        fclose(f);
42✔
1339

1340
        xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
42✔
1341
        f = fopen(p, "re");
42✔
1342
        if (!f)
42✔
1343
                return -errno;
×
1344

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

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

1361
        /* If the UID and GID mapping doesn't match, we don't support this mapping. */
1362
        if (uid_base != (uid_t) gid_base)
42✔
1363
                return -ENXIO;
1364
        if (uid_shift != (uid_t) gid_shift)
42✔
1365
                return -ENXIO;
1366
        if (uid_range != (uid_t) gid_range)
42✔
1367
                return -ENXIO;
1368

1369
        r = pidref_verify(&m->leader);
42✔
1370
        if (r < 0)
42✔
1371
                return r;
1372

1373
        *ret = uid_shift;
42✔
1374
        return 0;
42✔
1375
}
1376

1377
static int machine_owns_uid_internal(
290✔
1378
                Machine *machine,
1379
                const char *map_file, /* "uid_map" or "gid_map" */
1380
                uid_t uid,
1381
                uid_t *ret_internal_uid) {
1382

1383
        _cleanup_fclose_ FILE *f = NULL;
290✔
1384
        const char *p;
290✔
1385
        int r;
290✔
1386

1387
        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
1388
        assert_cc(sizeof(uid_t) == sizeof(gid_t));
290✔
1389

1390
        assert(machine);
290✔
1391

1392
        /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
1393
         * internally in the machine */
1394

1395
        if (machine->class != MACHINE_CONTAINER)
290✔
1396
                goto negative;
256✔
1397

1398
        p = procfs_file_alloca(machine->leader.pid, map_file);
34✔
1399
        f = fopen(p, "re");
34✔
1400
        if (!f) {
34✔
1401
                log_debug_errno(errno, "Failed to open %s, ignoring.", p);
×
1402
                goto negative;
×
1403
        }
1404

1405
        for (;;) {
98✔
1406
                uid_t uid_base, uid_shift, uid_range, converted;
66✔
1407

1408
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
66✔
1409
                if (r == -ENOMSG)
66✔
1410
                        break;
1411
                if (r < 0)
34✔
1412
                        return r;
2✔
1413

1414
                /* The private user namespace is disabled, ignoring. */
1415
                if (uid_shift == 0)
34✔
1416
                        continue;
32✔
1417

1418
                if (uid < uid_shift || uid >= uid_shift + uid_range)
34✔
1419
                        continue;
32✔
1420

1421
                converted = (uid - uid_shift + uid_base);
2✔
1422
                if (!uid_is_valid(converted))
2✔
1423
                        return -EINVAL;
1424

1425
                r = pidref_verify(&machine->leader);
2✔
1426
                if (r < 0)
2✔
1427
                        return r;
1428

1429
                if (ret_internal_uid)
2✔
1430
                        *ret_internal_uid = converted;
2✔
1431

1432
                return true;
1433
        }
1434

1435
negative:
288✔
1436
        if (ret_internal_uid)
288✔
1437
                *ret_internal_uid = UID_INVALID;
288✔
1438

1439
        return false;
1440
}
1441

1442
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
146✔
1443
        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
146✔
1444
}
1445

1446
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
144✔
1447
        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
144✔
1448
}
1449

1450
static int machine_translate_uid_internal(
4✔
1451
                Machine *machine,
1452
                const char *map_file, /* "uid_map" or "gid_map" */
1453
                uid_t uid,
1454
                uid_t *ret_host_uid) {
1455

1456
        _cleanup_fclose_ FILE *f = NULL;
4✔
1457
        const char *p;
4✔
1458
        int r;
4✔
1459

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

1463
        assert(machine);
4✔
1464
        assert(uid_is_valid(uid));
4✔
1465

1466
        if (machine->class != MACHINE_CONTAINER)
4✔
1467
                return -ESRCH;
1468

1469
        /* Translates a machine UID into a host UID */
1470

1471
        p = procfs_file_alloca(machine->leader.pid, map_file);
4✔
1472
        f = fopen(p, "re");
4✔
1473
        if (!f)
4✔
1474
                return -errno;
×
1475

1476
        for (;;) {
4✔
1477
                uid_t uid_base, uid_shift, uid_range, converted;
4✔
1478

1479
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
4✔
1480
                if (r == -ENOMSG)
4✔
1481
                        break;
1482
                if (r < 0)
4✔
1483
                        return r;
4✔
1484

1485
                if (uid < uid_base || uid >= uid_base + uid_range)
4✔
1486
                        continue;
×
1487

1488
                converted = uid - uid_base + uid_shift;
4✔
1489
                if (!uid_is_valid(converted))
4✔
1490
                        return -EINVAL;
1491

1492
                r = pidref_verify(&machine->leader);
4✔
1493
                if (r < 0)
4✔
1494
                        return r;
1495

1496
                if (ret_host_uid)
4✔
1497
                        *ret_host_uid = converted;
4✔
1498

1499
                return 0;
1500
        }
1501

1502
        return -ESRCH;
×
1503
}
1504

1505
int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
2✔
1506
        return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
2✔
1507
}
1508

1509
int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
2✔
1510
        return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
2✔
1511
}
1512

1513
int machine_open_root_directory(Machine *machine) {
3✔
1514
        int r;
3✔
1515

1516
        assert(machine);
3✔
1517

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

1524
                return fd;
1525
        }
1526

1527
        case MACHINE_CONTAINER: {
1✔
1528
                _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
1✔
1529
                _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
2✔
1530
                pid_t child;
1✔
1531

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

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

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

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

1564
                        errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1✔
1565
                        fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
1✔
1566

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

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

1580
                        _exit(EXIT_SUCCESS);
1✔
1581
                }
1582

1583
                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1✔
1584
                fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
1✔
1585

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

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

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

1598
                return fd;
1599
        }
1600

1601
        default:
1602
                return -EOPNOTSUPP;
1603
        }
1604
}
1605

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

1612
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
671✔
1613

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

1620
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
87✔
1621

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

1628
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
93✔
1629

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

1636
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