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

systemd / systemd / 17900778576

21 Sep 2025 07:54PM UTC coverage: 72.174% (-0.002%) from 72.176%
17900778576

push

github

web-flow
Follow-ups for recent changes to creds (#39040)

2 of 18 new or added lines in 3 files covered. (11.11%)

342 existing lines in 37 files now uncovered.

302268 of 418806 relevant lines covered (72.17%)

1052566.14 hits per line

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

77.46
/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.h"
28
#include "mkdir-label.h"
29
#include "namespace-util.h"
30
#include "operation.h"
31
#include "parse-util.h"
32
#include "path-util.h"
33
#include "process-util.h"
34
#include "serialize.h"
35
#include "signal-util.h"
36
#include "socket-util.h"
37
#include "special.h"
38
#include "stdio-util.h"
39
#include "string-table.h"
40
#include "string-util.h"
41
#include "strv.h"
42
#include "terminal-util.h"
43
#include "tmpfile-util.h"
44
#include "uid-range.h"
45
#include "unit-name.h"
46
#include "user-util.h"
47

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

51
        assert(class < _MACHINE_CLASS_MAX);
116✔
52
        assert(ret);
116✔
53

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

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

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

69
        if (name) {
116✔
70
                m->name = strdup(name);
114✔
71
                if (!m->name)
114✔
72
                        return -ENOMEM;
73
        }
74

75
        *ret = TAKE_PTR(m);
116✔
76
        return 0;
116✔
77
}
78

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

82
        assert(manager);
116✔
83
        assert(machine);
116✔
84

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

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

95
                free_and_replace(machine->state_file, temp);
108✔
96
        }
97

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

102
        machine->manager = manager;
116✔
103

104
        return 0;
116✔
105
}
106

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

111
        while (m->operations)
116✔
112
                operation_free(m->operations);
×
113

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

119
        if (m->manager) {
116✔
120
                machine_release_unit(m);
116✔
121

122
                (void) hashmap_remove(m->manager->machines, m->name);
116✔
123

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

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

135
        m->supervisor_pidfd_event_source = sd_event_source_disable_unref(m->supervisor_pidfd_event_source);
116✔
136
        pidref_done(&m->supervisor);
116✔
137

138
        sd_bus_message_unref(m->create_message);
116✔
139

140
        m->cgroup_empty_event_source = sd_event_source_disable_unref(m->cgroup_empty_event_source);
116✔
141

142
        free(m->name);
116✔
143

144
        free(m->state_file);
116✔
145
        free(m->service);
116✔
146
        free(m->root_directory);
116✔
147

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

153
        free(m->netif);
116✔
154
        free(m->ssh_address);
116✔
155
        free(m->ssh_private_key_path);
116✔
156

157
        return mfree(m);
116✔
158
}
159

160
int machine_save(Machine *m) {
347✔
161
        int r;
347✔
162

163
        assert(m);
347✔
164

165
        if (!m->state_file)
347✔
166
                return 0;
347✔
167

168
        if (!m->started)
347✔
169
                return 0;
170

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

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

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

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

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

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

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

205
        if (!sd_id128_is_null(m->id))
347✔
206
                fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
345✔
207

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

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

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

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

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

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

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

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

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

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

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

263
        return 0;
264
}
265

266
static void machine_unlink(Machine *m) {
156✔
267
        assert(m);
156✔
268

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

274
        if (m->state_file)
156✔
275
                (void) unlink(m->state_file);
156✔
276
}
156✔
277

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

284
        int r;
×
285

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

289
        pidref_done(pidref);
×
290

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

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

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

316
        assert(m);
×
317

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

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

346
        if (!streq_ptr(name, m->name))
×
347
                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);
×
348

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

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

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

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

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

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

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

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

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

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

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

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

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

412
        return r;
413
}
414

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

497
        r = sd_bus_message_close_container(m);
86✔
498
        if (r < 0)
86✔
499
                return r;
500

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

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

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

516
        machine->unit = TAKE_PTR(unit);
86✔
517
        machine->referenced = true;
86✔
518

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

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

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

530
        assert(m);
108✔
531
        assert(m->class != MACHINE_HOST);
108✔
532

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

539
        assert(m->unit);
108✔
540

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

547
        return 0;
548
}
549

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

553
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
106✔
554
        machine_add_to_gc_queue(m);
106✔
555

556
        return 0;
106✔
557
}
558

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

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

UNCOV
565
        return 0;
×
566
}
567

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

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

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

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

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

587
        (void) sd_event_source_set_description(*source, "machine-pidfd");
214✔
588

589
        return 0;
214✔
590
}
591

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

596
        assert(m->cgroup);
17✔
597

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

602
        if (r > 0)
17✔
603
                machine_add_to_gc_queue(m);
17✔
604

605
        return 0;
606
}
607

608
static int machine_watch_cgroup(Machine *m) {
108✔
609
        int r;
108✔
610

611
        assert(m);
108✔
612
        assert(!m->cgroup_empty_event_source);
108✔
613

614
        if (!m->cgroup)
108✔
615
                return 0;
108✔
616

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

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

626
        return 0;
627
}
628

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

632
        assert(m);
116✔
633

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

637
        if (m->started)
108✔
638
                return 0;
639

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

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

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

652
        r = machine_watch_cgroup(m);
108✔
653
        if (r < 0)
108✔
654
                return r;
655

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

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

667
        if (!dual_timestamp_is_set(&m->timestamp))
108✔
668
                dual_timestamp_now(&m->timestamp);
108✔
669

670
        m->started = true;
108✔
671

672
        /* Save new machine data */
673
        machine_save(m);
108✔
674

675
        machine_send_signal(m, true);
108✔
676

677
        return 0;
108✔
678
}
679

680
int machine_stop(Machine *m) {
67✔
681
        int r;
67✔
682

683
        assert(m);
67✔
684

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

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

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

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

699
                free_and_replace(m->scope_job, job);
57✔
700

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

709
        m->stopping = true;
67✔
710

711
        machine_save(m);
67✔
712

713
        return 0;
67✔
714
}
715

716
int machine_finalize(Machine *m) {
156✔
717
        assert(m);
156✔
718

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

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

729
        machine_unlink(m);
156✔
730
        machine_add_to_gc_queue(m);
156✔
731

732
        if (m->started) {
156✔
733
                machine_send_signal(m, false);
106✔
734
                m->started = false;
106✔
735
        }
736

737
        return 0;
156✔
738
}
739

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

743
        assert(m);
700✔
744

745
        if (m->class == MACHINE_HOST)
700✔
746
                return false;
747

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

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

759
        if (m->scope_job) {
158✔
760
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
47✔
761

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

769
        if (m->unit && !m->subgroup) {
146✔
770
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
106✔
771

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

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

787
        return true;
788
}
789

790
void machine_add_to_gc_queue(Machine *m) {
969✔
791
        assert(m);
969✔
792

793
        if (m->in_gc_queue)
969✔
794
                return;
795

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

799
        manager_enqueue_gc(m->manager);
456✔
800
}
801

802
MachineState machine_get_state(Machine *s) {
149✔
803
        assert(s);
149✔
804

805
        if (s->class == MACHINE_HOST)
149✔
806
                return MACHINE_RUNNING;
807

808
        if (s->stopping)
149✔
809
                return MACHINE_CLOSING;
810

811
        if (s->scope_job)
96✔
UNCOV
812
                return MACHINE_OPENING;
×
813

814
        return MACHINE_RUNNING;
815
}
816

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

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

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

825
        switch (whom) {
21✔
826

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

930
        return 0;
931
}
932

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1096
        return 0;
1097
}
1098

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

1103
        assert(user);
1✔
1104

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1254
void machine_release_unit(Machine *m) {
116✔
1255
        assert(m);
116✔
1256

1257
        if (!m->unit)
116✔
1258
                return;
1259

1260
        assert(m->manager);
116✔
1261

1262
        if (m->referenced) {
116✔
UNCOV
1263
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1264
                int r;
86✔
1265

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

1272
                m->referenced = false;
86✔
1273
        }
1274

1275
        if (!m->subgroup)
116✔
1276
                (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
96✔
1277

1278
        m->unit = mfree(m->unit);
116✔
1279

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

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

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

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

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

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

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

UNCOV
1317
                return -errno;
×
1318
        }
1319

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

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

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

1339
        fclose(f);
42✔
1340

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

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

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

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

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

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

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

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

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

1391
        assert(machine);
302✔
1392

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

1396
        if (machine->class != MACHINE_CONTAINER)
302✔
1397
                goto negative;
268✔
1398

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

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

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

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

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

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

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

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

1433
                return true;
1434
        }
1435

1436
negative:
300✔
1437
        if (ret_internal_uid)
300✔
1438
                *ret_internal_uid = UID_INVALID;
300✔
1439

1440
        return false;
1441
}
1442

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1500
                return 0;
1501
        }
1502

UNCOV
1503
        return -ESRCH;
×
1504
}
1505

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

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

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

1517
        assert(machine);
3✔
1518

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

1525
                return fd;
1526
        }
1527

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

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

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

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

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

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

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

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

1581
                        _exit(EXIT_SUCCESS);
1✔
1582
                }
1583

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

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

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

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

1599
                return fd;
1600
        }
1601

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

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

1613
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
693✔
1614

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

1621
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
87✔
1622

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

1629
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
93✔
1630

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

1637
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