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

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

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

79.69
/src/machine/machine.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <unistd.h>
5
#include <sys/stat.h>
6

7
#include "sd-messages.h"
8

9
#include "alloc-util.h"
10
#include "bus-error.h"
11
#include "bus-internal.h"
12
#include "bus-locator.h"
13
#include "bus-unit-util.h"
14
#include "bus-util.h"
15
#include "env-file.h"
16
#include "errno-util.h"
17
#include "escape.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 "machine-dbus.h"
25
#include "machine.h"
26
#include "machined.h"
27
#include "mkdir-label.h"
28
#include "namespace-util.h"
29
#include "operation.h"
30
#include "parse-util.h"
31
#include "path-util.h"
32
#include "process-util.h"
33
#include "serialize.h"
34
#include "socket-util.h"
35
#include "special.h"
36
#include "stdio-util.h"
37
#include "string-table.h"
38
#include "string-util.h"
39
#include "terminal-util.h"
40
#include "tmpfile-util.h"
41
#include "uid-range.h"
42
#include "unit-name.h"
43
#include "user-util.h"
44

45
int machine_new(MachineClass class, const char *name, Machine **ret) {
91✔
46
        _cleanup_(machine_freep) Machine *m = NULL;
91✔
47

48
        assert(class < _MACHINE_CLASS_MAX);
91✔
49
        assert(ret);
91✔
50

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

55
        m = new(Machine, 1);
91✔
56
        if (!m)
91✔
57
                return -ENOMEM;
58

59
        *m = (Machine) {
91✔
60
                .class = class,
61
                .leader = PIDREF_NULL,
62
                .vsock_cid = VMADDR_CID_ANY,
63
        };
64

65
        if (name) {
91✔
66
                m->name = strdup(name);
89✔
67
                if (!m->name)
89✔
68
                        return -ENOMEM;
69
        }
70

71
        *ret = TAKE_PTR(m);
91✔
72
        return 0;
91✔
73
}
74

75
int machine_link(Manager *manager, Machine *machine) {
91✔
76
        int r;
91✔
77

78
        assert(manager);
91✔
79
        assert(machine);
91✔
80

81
        if (machine->manager)
91✔
82
                return -EEXIST;
83
        if (!machine->name)
91✔
84
                return -EINVAL;
85

86
        if (machine->class != MACHINE_HOST) {
91✔
87
                char *temp = path_join("/run/systemd/machines", machine->name);
85✔
88
                if (!temp)
85✔
89
                        return -ENOMEM;
91✔
90

91
                free_and_replace(machine->state_file, temp);
85✔
92
        }
93

94
        r = hashmap_put(manager->machines, machine->name, machine);
91✔
95
        if (r < 0)
91✔
96
                return r;
97

98
        machine->manager = manager;
91✔
99

100
        return 0;
91✔
101
}
102

103
Machine* machine_free(Machine *m) {
91✔
104
        if (!m)
91✔
105
                return NULL;
106

107
        while (m->operations)
91✔
108
                operation_free(m->operations);
×
109

110
        if (m->in_gc_queue) {
91✔
111
                assert(m->manager);
85✔
112
                LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
85✔
113
        }
114

115
        if (m->manager) {
91✔
116
                machine_release_unit(m);
91✔
117

118
                (void) hashmap_remove(m->manager->machines, m->name);
91✔
119

120
                if (m->manager->host_machine == m)
91✔
121
                        m->manager->host_machine = NULL;
6✔
122
        }
123

124
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
91✔
125
        if (pidref_is_set(&m->leader)) {
91✔
126
                if (m->manager)
91✔
127
                        (void) hashmap_remove_value(m->manager->machines_by_leader, &m->leader, m);
91✔
128
                pidref_done(&m->leader);
91✔
129
        }
130

131
        sd_bus_message_unref(m->create_message);
91✔
132

133
        free(m->name);
91✔
134
        free(m->scope_job);
91✔
135
        free(m->state_file);
91✔
136
        free(m->service);
91✔
137
        free(m->root_directory);
91✔
138
        free(m->netif);
91✔
139
        free(m->ssh_address);
91✔
140
        free(m->ssh_private_key_path);
91✔
141
        return mfree(m);
91✔
142
}
143

144
int machine_save(Machine *m) {
276✔
145
        _cleanup_(unlink_and_freep) char *temp_path = NULL;
×
146
        _cleanup_fclose_ FILE *f = NULL;
276✔
147
        int r;
276✔
148

149
        assert(m);
276✔
150

151
        if (!m->state_file)
276✔
152
                return 0;
153

154
        if (!m->started)
276✔
155
                return 0;
156

157
        r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
276✔
158
        if (r < 0)
276✔
159
                goto fail;
×
160

161
        r = fopen_temporary(m->state_file, &f, &temp_path);
276✔
162
        if (r < 0)
276✔
163
                goto fail;
×
164

165
        (void) fchmod(fileno(f), 0644);
276✔
166

167
        fprintf(f,
276✔
168
                "# This is private data. Do not parse.\n"
169
                "NAME=%s\n",
170
                m->name);
171

172
        if (m->unit) {
276✔
173
                _cleanup_free_ char *escaped = NULL;
276✔
174

175
                escaped = cescape(m->unit);
276✔
176
                if (!escaped) {
276✔
177
                        r = -ENOMEM;
×
178
                        goto fail;
×
179
                }
180

181
                fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
276✔
182
        }
183

184
        if (m->scope_job)
276✔
185
                fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
123✔
186

187
        if (m->service) {
276✔
188
                _cleanup_free_ char *escaped = NULL;
274✔
189

190
                escaped = cescape(m->service);
274✔
191
                if (!escaped) {
274✔
192
                        r = -ENOMEM;
×
193
                        goto fail;
×
194
                }
195
                fprintf(f, "SERVICE=%s\n", escaped);
274✔
196
        }
197

198
        if (m->root_directory) {
276✔
199
                _cleanup_free_ char *escaped = NULL;
266✔
200

201
                escaped = cescape(m->root_directory);
266✔
202
                if (!escaped) {
266✔
203
                        r = -ENOMEM;
×
204
                        goto fail;
×
205
                }
206
                fprintf(f, "ROOT=%s\n", escaped);
266✔
207
        }
208

209
        if (!sd_id128_is_null(m->id))
276✔
210
                fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
274✔
211

212
        if (pidref_is_set(&m->leader))
276✔
213
                fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
276✔
214

215
        if (m->class != _MACHINE_CLASS_INVALID)
276✔
216
                fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
276✔
217

218
        if (dual_timestamp_is_set(&m->timestamp))
276✔
219
                fprintf(f,
276✔
220
                        "REALTIME="USEC_FMT"\n"
221
                        "MONOTONIC="USEC_FMT"\n",
222
                        m->timestamp.realtime,
223
                        m->timestamp.monotonic);
224

225
        if (m->n_netif > 0) {
276✔
226
                size_t i;
51✔
227

228
                fputs("NETIF=", f);
51✔
229

230
                for (i = 0; i < m->n_netif; i++) {
102✔
231
                        if (i != 0)
51✔
232
                                fputc(' ', f);
×
233

234
                        fprintf(f, "%i", m->netif[i]);
51✔
235
                }
236

237
                fputc('\n', f);
51✔
238
        }
239

240
        r = fflush_and_check(f);
276✔
241
        if (r < 0)
276✔
242
                goto fail;
×
243

244
        if (rename(temp_path, m->state_file) < 0) {
276✔
245
                r = -errno;
×
246
                goto fail;
×
247
        }
248

249
        temp_path = mfree(temp_path);
276✔
250

251
        if (m->unit) {
276✔
252
                char *sl;
276✔
253

254
                /* Create a symlink from the unit name to the machine
255
                 * name, so that we can quickly find the machine for
256
                 * each given unit. Ignore error. */
257
                sl = strjoina("/run/systemd/machines/unit:", m->unit);
1,380✔
258
                (void) symlink(m->name, sl);
276✔
259
        }
260

261
        return 0;
262

263
fail:
×
264
        (void) unlink(m->state_file);
×
265

266
        return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
×
267
}
268

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

272
        if (m->unit) {
123✔
273
                char *sl;
123✔
274

275
                sl = strjoina("/run/systemd/machines/unit:", m->unit);
615✔
276
                (void) unlink(sl);
123✔
277
        }
278

279
        if (m->state_file)
123✔
280
                (void) unlink(m->state_file);
123✔
281
}
123✔
282

283
int machine_load(Machine *m) {
×
284
        _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
×
285
        int r;
×
286

287
        assert(m);
×
288

289
        if (!m->state_file)
×
290
                return 0;
291

292
        r = parse_env_file(NULL, m->state_file,
×
293
                           "SCOPE",     &m->unit,
294
                           "SCOPE_JOB", &m->scope_job,
295
                           "SERVICE",   &m->service,
296
                           "ROOT",      &m->root_directory,
297
                           "ID",        &id,
298
                           "LEADER",    &leader,
299
                           "CLASS",     &class,
300
                           "REALTIME",  &realtime,
301
                           "MONOTONIC", &monotonic,
302
                           "NETIF",     &netif);
303
        if (r == -ENOENT)
×
304
                return 0;
305
        if (r < 0)
×
306
                return log_error_errno(r, "Failed to read %s: %m", m->state_file);
×
307

308
        if (id)
×
309
                (void) sd_id128_from_string(id, &m->id);
×
310

311
        if (leader) {
×
312
                pidref_done(&m->leader);
×
313
                r = pidref_set_pidstr(&m->leader, leader);
×
314
                if (r < 0)
×
315
                        log_debug_errno(r, "Failed to set leader PID to '%s', ignoring: %m", leader);
×
316
        }
317

318
        if (class) {
×
319
                MachineClass c;
×
320

321
                c = machine_class_from_string(class);
×
322
                if (c >= 0)
×
323
                        m->class = c;
×
324
        }
325

326
        if (realtime)
×
327
                (void) deserialize_usec(realtime, &m->timestamp.realtime);
×
328
        if (monotonic)
×
329
                (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
×
330

331
        if (netif) {
×
332
                _cleanup_free_ int *ni = NULL;
×
333
                size_t nr = 0;
×
334
                const char *p;
×
335

336
                p = netif;
×
337
                for (;;) {
×
338
                        _cleanup_free_ char *word = NULL;
×
339

340
                        r = extract_first_word(&p, &word, NULL, 0);
×
341
                        if (r == 0)
×
342
                                break;
343
                        if (r == -ENOMEM)
×
344
                                return log_oom();
×
345
                        if (r < 0) {
×
346
                                log_warning_errno(r, "Failed to parse NETIF: %s", netif);
×
347
                                break;
348
                        }
349

350
                        r = parse_ifindex(word);
×
351
                        if (r < 0)
×
352
                                continue;
×
353

354
                        if (!GREEDY_REALLOC(ni, nr + 1))
×
355
                                return log_oom();
×
356

357
                        ni[nr++] = r;
×
358
                }
359

360
                free_and_replace(m->netif, ni);
×
361
                m->n_netif = nr;
×
362
        }
363

364
        return r;
365
}
366

367
static int machine_start_scope(
66✔
368
                Machine *machine,
369
                bool allow_pidfd,
370
                sd_bus_message *more_properties,
371
                sd_bus_error *error) {
372

373
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
132✔
374
        _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
×
375
        _cleanup_free_ char *escaped = NULL, *unit = NULL;
66✔
376
        const char *description;
66✔
377
        int r;
66✔
378

379
        assert(machine);
66✔
380
        assert(pidref_is_set(&machine->leader));
66✔
381
        assert(!machine->unit);
66✔
382

383
        escaped = unit_name_escape(machine->name);
66✔
384
        if (!escaped)
66✔
385
                return log_oom();
×
386

387
        unit = strjoin("machine-", escaped, ".scope");
66✔
388
        if (!unit)
66✔
389
                return log_oom();
×
390

391
        r = bus_message_new_method_call(
132✔
392
                        machine->manager->bus,
66✔
393
                        &m,
394
                        bus_systemd_mgr,
395
                        "StartTransientUnit");
396
        if (r < 0)
66✔
397
                return r;
398

399
        r = sd_bus_message_append(m, "ss", unit, "fail");
66✔
400
        if (r < 0)
66✔
401
                return r;
402

403
        r = sd_bus_message_open_container(m, 'a', "(sv)");
66✔
404
        if (r < 0)
66✔
405
                return r;
406

407
        r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
66✔
408
        if (r < 0)
66✔
409
                return r;
410

411
        description = strjoina(machine->class == MACHINE_VM ? "Virtual Machine " : "Container ", machine->name);
396✔
412
        r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
66✔
413
        if (r < 0)
66✔
414
                return r;
415

416
        r = bus_append_scope_pidref(m, &machine->leader, allow_pidfd);
66✔
417
        if (r < 0)
66✔
418
                return r;
419

420
        r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
66✔
421
                                  "Delegate", "b", 1,
422
                                  "CollectMode", "s", "inactive-or-failed",
423
                                  "AddRef", "b", 1,
424
                                  "TasksMax", "t", UINT64_C(16384));
425
        if (r < 0)
66✔
426
                return r;
427

428
        if (more_properties) {
66✔
429
                r = sd_bus_message_copy(m, more_properties, true);
66✔
430
                if (r < 0)
66✔
431
                        return r;
432
        }
433

434
        r = sd_bus_message_close_container(m);
66✔
435
        if (r < 0)
66✔
436
                return r;
437

438
        r = sd_bus_message_append(m, "a(sa(sv))", 0);
66✔
439
        if (r < 0)
66✔
440
                return r;
441

442
        r = sd_bus_call(NULL, m, 0, &e, &reply);
66✔
443
        if (r < 0) {
66✔
444
                /* If this failed with a property we couldn't write, this is quite likely because the server
445
                 * doesn't support PIDFDs yet, let's try without. */
446
                if (allow_pidfd &&
×
447
                    sd_bus_error_has_names(&e, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
×
448
                        return machine_start_scope(machine, /* allow_pidfd = */ false, more_properties, error);
×
449

450
                return sd_bus_error_move(error, &e);
×
451
        }
452

453
        machine->unit = TAKE_PTR(unit);
66✔
454
        machine->referenced = true;
66✔
455

456
        const char *job;
66✔
457
        r = sd_bus_message_read(reply, "o", &job);
66✔
458
        if (r < 0)
66✔
459
                return r;
460

461
        return free_and_strdup(&machine->scope_job, job);
66✔
462
}
463

464
static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
85✔
465
        int r;
85✔
466

467
        assert(m);
85✔
468
        assert(m->class != MACHINE_HOST);
85✔
469

470
        if (!m->unit) {
85✔
471
                r = machine_start_scope(m, /* allow_pidfd = */ true, properties, error);
66✔
472
                if (r < 0)
66✔
473
                        return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
×
474
        }
475

476
        assert(m->unit);
85✔
477

478
        r = hashmap_ensure_put(&m->manager->machines_by_unit, &string_hash_ops, m->unit, m);
85✔
479
        if (r < 0)
85✔
480
                return r;
×
481

482
        return 0;
483
}
484

485
static int machine_dispatch_leader_pidfd(sd_event_source *s, int fd, unsigned revents, void *userdata) {
81✔
486
        Machine *m = ASSERT_PTR(userdata);
81✔
487

488
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
81✔
489
        machine_add_to_gc_queue(m);
81✔
490

491
        return 0;
81✔
492
}
493

494
static int machine_watch_pidfd(Machine *m) {
85✔
495
        int r;
85✔
496

497
        assert(m);
85✔
498
        assert(m->manager);
85✔
499
        assert(pidref_is_set(&m->leader));
85✔
500
        assert(!m->leader_pidfd_event_source);
85✔
501

502
        if (m->leader.fd < 0)
85✔
503
                return 0;
504

505
        /* If we have a pidfd for the leader, let's also track it for POLLIN, and GC the machine
506
         * automatically if it dies */
507

508
        r = sd_event_add_io(m->manager->event, &m->leader_pidfd_event_source, m->leader.fd, EPOLLIN, machine_dispatch_leader_pidfd, m);
85✔
509
        if (r < 0)
85✔
510
                return r;
511

512
        (void) sd_event_source_set_description(m->leader_pidfd_event_source, "machine-pidfd");
85✔
513

514
        return 0;
85✔
515
}
516

517
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
91✔
518
        int r;
91✔
519

520
        assert(m);
91✔
521

522
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
91✔
523
                return -EOPNOTSUPP;
524

525
        if (m->started)
85✔
526
                return 0;
527

528
        r = hashmap_ensure_put(&m->manager->machines_by_leader, &pidref_hash_ops, &m->leader, m);
85✔
529
        if (r < 0)
85✔
530
                return r;
531

532
        r = machine_watch_pidfd(m);
85✔
533
        if (r < 0)
85✔
534
                return r;
535

536
        /* Create cgroup */
537
        r = machine_ensure_scope(m, properties, error);
85✔
538
        if (r < 0)
85✔
539
                return r;
540

541
        log_struct(LOG_INFO,
85✔
542
                   LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START_STR),
543
                   LOG_ITEM("NAME=%s", m->name),
544
                   LOG_ITEM("LEADER="PID_FMT, m->leader.pid),
545
                   LOG_MESSAGE("New machine %s.", m->name));
546

547
        if (!dual_timestamp_is_set(&m->timestamp))
85✔
548
                dual_timestamp_now(&m->timestamp);
85✔
549

550
        m->started = true;
85✔
551

552
        /* Save new machine data */
553
        machine_save(m);
85✔
554

555
        machine_send_signal(m, true);
85✔
556

557
        return 0;
85✔
558
}
559

560
int machine_stop(Machine *m) {
57✔
561
        int r;
57✔
562

563
        assert(m);
57✔
564

565
        if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
57✔
566
                return -EOPNOTSUPP;
567

568
        if (m->unit) {
57✔
569
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
570
                char *job = NULL;
57✔
571

572
                r = manager_stop_unit(m->manager, m->unit, &error, &job);
57✔
573
                if (r < 0)
57✔
574
                        return log_error_errno(r, "Failed to stop machine scope: %s", bus_error_message(&error, r));
×
575

576
                free_and_replace(m->scope_job, job);
57✔
577
        }
578

579
        m->stopping = true;
57✔
580

581
        machine_save(m);
57✔
582

583
        return 0;
57✔
584
}
585

586
int machine_finalize(Machine *m) {
123✔
587
        assert(m);
123✔
588

589
        if (m->started) {
123✔
590
                log_struct(LOG_INFO,
85✔
591
                           LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP_STR),
592
                           LOG_ITEM("NAME=%s", m->name),
593
                           LOG_ITEM("LEADER="PID_FMT, m->leader.pid),
594
                           LOG_MESSAGE("Machine %s terminated.", m->name));
595

596
                m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
85✔
597
        }
598

599
        machine_unlink(m);
123✔
600
        machine_add_to_gc_queue(m);
123✔
601

602
        if (m->started) {
123✔
603
                machine_send_signal(m, false);
85✔
604
                m->started = false;
85✔
605
        }
606

607
        return 0;
123✔
608
}
609

610
bool machine_may_gc(Machine *m, bool drop_not_started) {
556✔
611
        assert(m);
556✔
612

613
        if (m->class == MACHINE_HOST)
556✔
614
                return false;
615

616
        if (!pidref_is_set(&m->leader))
536✔
617
                return true;
618

619
        if (drop_not_started && !m->started)
536✔
620
                return true;
621

622
        if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
460✔
623
                return false;
624

625
        if (m->unit && manager_unit_is_active(m->manager, m->unit))
429✔
626
                return false;
327✔
627

628
        return true;
629
}
630

631
void machine_add_to_gc_queue(Machine *m) {
942✔
632
        assert(m);
942✔
633

634
        if (m->in_gc_queue)
942✔
635
                return;
636

637
        LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
363✔
638
        m->in_gc_queue = true;
363✔
639

640
        manager_enqueue_gc(m->manager);
363✔
641
}
642

643
MachineState machine_get_state(Machine *s) {
124✔
644
        assert(s);
124✔
645

646
        if (s->class == MACHINE_HOST)
124✔
647
                return MACHINE_RUNNING;
648

649
        if (s->stopping)
124✔
650
                return MACHINE_CLOSING;
651

652
        if (s->scope_job)
84✔
653
                return MACHINE_OPENING;
×
654

655
        return MACHINE_RUNNING;
656
}
657

658
int machine_kill(Machine *m, KillWhom whom, int signo) {
21✔
659
        assert(m);
21✔
660

661
        if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
21✔
662
                return -EOPNOTSUPP;
663

664
        if (!m->unit)
21✔
665
                return -ESRCH;
666

667
        if (whom == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
21✔
668
                return pidref_kill(&m->leader, signo);
17✔
669

670
        /* Otherwise, make PID 1 do it for us, for the entire cgroup */
671
        return manager_kill_unit(m->manager, m->unit, signo, NULL);
4✔
672
}
673

674
int machine_openpt(Machine *m, int flags, char **ret_peer) {
8✔
675
        assert(m);
8✔
676

677
        switch (m->class) {
8✔
678

679
        case MACHINE_HOST:
8✔
680
                return openpt_allocate(flags, ret_peer);
8✔
681

682
        case MACHINE_CONTAINER:
683
                if (!pidref_is_set(&m->leader))
×
684
                        return -EINVAL;
685

686
                return openpt_allocate_in_namespace(&m->leader, flags, ret_peer);
×
687

688
        default:
689
                return -EOPNOTSUPP;
690
        }
691
}
692

693
static int machine_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
6✔
694
        int r;
6✔
695

696
        assert(m);
6✔
697
        assert(ret);
6✔
698

699
        switch (m->class) {
6✔
700

701
        case MACHINE_HOST:
6✔
702
                *ret = NULL;
6✔
703
                return 0;
6✔
704

705
        case MACHINE_CONTAINER: {
×
706
                _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
×
707
                char *address;
×
708

709
                r = sd_bus_new(&bus);
×
710
                if (r < 0)
×
711
                        return log_debug_errno(r, "Failed to allocate new DBus: %m");
×
712

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

716
                bus->address = address;
×
717
                bus->bus_client = true;
×
718
                bus->trusted = false;
×
719
                bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
×
720

721
                r = sd_bus_start(bus);
×
722
                if (r == -ENOENT)
×
723
                        return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
×
724
                if (r < 0)
×
725
                        return r;
726

727
                *ret = TAKE_PTR(bus);
×
728
                return 0;
×
729
        }
730

731
        default:
732
                return -EOPNOTSUPP;
733
        }
734
}
735

736
int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error) {
1✔
737
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
1✔
738
        sd_bus *container_bus = NULL;
1✔
739
        const char *p, *getty;
1✔
740
        int r;
1✔
741

742
        assert(m);
1✔
743
        assert(ptmx_name);
1✔
744

745
        p = path_startswith(ptmx_name, "/dev/pts/");
1✔
746
        if (!p)
1✔
747
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
748

749
        r = machine_bus_new(m, error, &allocated_bus);
1✔
750
        if (r < 0)
1✔
751
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
752

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

756
        r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, /* reply = */ NULL, "ss", getty, "replace");
1✔
757
        if (r < 0)
1✔
758
                return log_debug_errno(r, "Failed to StartUnit '%s' in container '%s': %m", getty, m->name);
×
759

760
        return 0;
761
}
762

763
int machine_start_shell(
5✔
764
                Machine *m,
765
                int ptmx_fd,
766
                const char *ptmx_name,
767
                const char *user,
768
                const char *path,
769
                char **args,
770
                char **env,
771
                sd_bus_error *error) {
772
        _cleanup_close_ int pty_fd = -EBADF;
5✔
773
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *tm = NULL;
5✔
774
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
5✔
775
        const char *p, *utmp_id, *unit, *description;
5✔
776
        sd_bus *container_bus = NULL;
5✔
777
        int r;
5✔
778

779
        assert(m);
5✔
780
        assert(ptmx_fd >= 0);
5✔
781
        assert(ptmx_name);
5✔
782

783
        if (isempty(user) || isempty(path) || strv_isempty(args))
20✔
784
                return -EINVAL;
785

786
        p = path_startswith(ptmx_name, "/dev/pts/");
5✔
787
        utmp_id = path_startswith(ptmx_name, "/dev/");
5✔
788
        if (!p || !utmp_id)
5✔
789
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
790

791
        pty_fd = pty_open_peer(ptmx_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
5✔
792
        if (pty_fd < 0)
5✔
793
                return log_debug_errno(pty_fd, "Failed to open terminal: %m");
×
794

795
        r = machine_bus_new(m, error, &allocated_bus);
5✔
796
        if (r < 0)
5✔
797
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
798

799
        container_bus = allocated_bus ?: m->manager->bus;
5✔
800
        r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
5✔
801
        if (r < 0)
5✔
802
                return r;
803

804
        /* Name and mode */
805
        unit = strjoina("container-shell@", p, ".service");
35✔
806
        r = sd_bus_message_append(tm, "ss", unit, "fail");
5✔
807
        if (r < 0)
5✔
808
                return r;
809

810
        /* Properties */
811
        r = sd_bus_message_open_container(tm, 'a', "(sv)");
5✔
812
        if (r < 0)
5✔
813
                return r;
814

815
        description = strjoina("Shell for User ", user);
25✔
816
        r = sd_bus_message_append(tm,
5✔
817
                                  "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
818
                                  "Description", "s", description,
819
                                  "StandardInputFileDescriptor", "h", pty_fd,
820
                                  "StandardOutputFileDescriptor", "h", pty_fd,
821
                                  "StandardErrorFileDescriptor", "h", pty_fd,
822
                                  "SendSIGHUP", "b", true,
823
                                  "IgnoreSIGPIPE", "b", false,
824
                                  "KillMode", "s", "mixed",
825
                                  "TTYPath", "s", ptmx_name,
826
                                  "TTYReset", "b", true,
827
                                  "UtmpIdentifier", "s", utmp_id,
828
                                  "UtmpMode", "s", "user",
829
                                  "PAMName", "s", "login",
830
                                  "WorkingDirectory", "s", "-~");
831
        if (r < 0)
5✔
832
                return r;
833

834
        r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
5✔
835
        if (r < 0)
5✔
836
                return r;
837

838
        if (!strv_isempty(env)) {
5✔
839
                r = sd_bus_message_open_container(tm, 'r', "sv");
4✔
840
                if (r < 0)
4✔
841
                        return r;
842

843
                r = sd_bus_message_append(tm, "s", "Environment");
4✔
844
                if (r < 0)
4✔
845
                        return r;
846

847
                r = sd_bus_message_open_container(tm, 'v', "as");
4✔
848
                if (r < 0)
4✔
849
                        return r;
850

851
                r = sd_bus_message_append_strv(tm, env);
4✔
852
                if (r < 0)
4✔
853
                        return r;
854

855
                r = sd_bus_message_close_container(tm);
4✔
856
                if (r < 0)
4✔
857
                        return r;
858

859
                r = sd_bus_message_close_container(tm);
4✔
860
                if (r < 0)
4✔
861
                        return r;
862
        }
863

864
        /* Exec container */
865
        r = sd_bus_message_open_container(tm, 'r', "sv");
5✔
866
        if (r < 0)
5✔
867
                return r;
868

869
        r = sd_bus_message_append(tm, "s", "ExecStart");
5✔
870
        if (r < 0)
5✔
871
                return r;
872

873
        r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
5✔
874
        if (r < 0)
5✔
875
                return r;
876

877
        r = sd_bus_message_open_container(tm, 'a', "(sasb)");
5✔
878
        if (r < 0)
5✔
879
                return r;
880

881
        r = sd_bus_message_open_container(tm, 'r', "sasb");
5✔
882
        if (r < 0)
5✔
883
                return r;
884

885
        r = sd_bus_message_append(tm, "s", path);
5✔
886
        if (r < 0)
5✔
887
                return r;
888

889
        r = sd_bus_message_append_strv(tm, args);
5✔
890
        if (r < 0)
5✔
891
                return r;
892

893
        r = sd_bus_message_append(tm, "b", true);
5✔
894
        if (r < 0)
5✔
895
                return r;
896

897
        r = sd_bus_message_close_container(tm);
5✔
898
        if (r < 0)
5✔
899
                return r;
900

901
        r = sd_bus_message_close_container(tm);
5✔
902
        if (r < 0)
5✔
903
                return r;
904

905
        r = sd_bus_message_close_container(tm);
5✔
906
        if (r < 0)
5✔
907
                return r;
908

909
        r = sd_bus_message_close_container(tm);
5✔
910
        if (r < 0)
5✔
911
                return r;
912

913
        r = sd_bus_message_close_container(tm);
5✔
914
        if (r < 0)
5✔
915
                return r;
916

917
        /* Auxiliary units */
918
        r = sd_bus_message_append(tm, "a(sa(sv))", 0);
5✔
919
        if (r < 0)
5✔
920
                return r;
921

922
        r = sd_bus_call(container_bus, tm, 0, error, NULL);
5✔
923
        if (r < 0)
5✔
924
                return r;
×
925

926
        return 0;
927
}
928

929
char** machine_default_shell_args(const char *user) {
1✔
930
        _cleanup_strv_free_ char **args = NULL;
1✔
931
        int r;
1✔
932

933
        assert(user);
1✔
934

935
        args = new0(char*, 3 + 1);
1✔
936
        if (!args)
1✔
937
                return NULL;
938

939
        args[0] = strdup("sh");
1✔
940
        if (!args[0])
1✔
941
                return NULL;
942

943
        args[1] = strdup("-c");
1✔
944
        if (!args[1])
1✔
945
                return NULL;
946

947
        r = asprintf(&args[2],
1✔
948
                     "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
949
                     "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
950
                     user);
951
        if (r < 0) {
1✔
952
                args[2] = NULL;
×
953
                return NULL;
×
954
        }
955

956
        return TAKE_PTR(args);
1✔
957
}
958

959
int machine_copy_from_to_operation(
8✔
960
                Manager *manager,
961
                Machine *machine,
962
                const char *host_path,
963
                const char *container_path,
964
                bool copy_from_container,
965
                CopyFlags copy_flags,
966
                Operation **ret) {
967

968
        _cleanup_close_ int host_fd = -EBADF, target_mntns_fd = -EBADF, source_mntns_fd = -EBADF;
16✔
969
        _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
8✔
970
        _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
8✔
971
        _cleanup_(sigkill_waitp) pid_t child = 0;
×
972
        uid_t uid_shift;
8✔
973
        int r;
8✔
974

975
        assert(manager);
8✔
976
        assert(machine);
8✔
977
        assert(ret);
8✔
978

979
        if (isempty(host_path) || isempty(container_path))
24✔
980
                return -EINVAL;
981

982
        r = path_extract_filename(host_path, &host_basename);
8✔
983
        if (r < 0)
8✔
984
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", host_path);
×
985

986
        r = path_extract_filename(container_path, &container_basename);
8✔
987
        if (r < 0)
8✔
988
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path);
×
989

990
        host_fd = open_parent(host_path, O_CLOEXEC, 0);
8✔
991
        if (host_fd < 0)
8✔
992
                return log_debug_errno(host_fd, "Failed to open host directory '%s': %m", host_path);
×
993

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

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

1002
        source_mntns_fd = namespace_open_by_type(NAMESPACE_MOUNT);
8✔
1003
        if (source_mntns_fd < 0)
8✔
1004
                return log_debug_errno(source_mntns_fd, "Failed to open our own mount namespace: %m");
×
1005

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

1009
        r = namespace_fork("(sd-copyns)",
8✔
1010
                           "(sd-copy)",
1011
                           /* except_fds = */ NULL,
1012
                           /* n_except_fds = */ 0,
1013
                           FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
1014
                           /* pidns_fd = */ -EBADF,
1015
                           target_mntns_fd,
1016
                           /* netns_fd = */ -EBADF,
1017
                           /* userns_fd = */ -EBADF,
1018
                           /* root_fd = */ -EBADF,
1019
                           &child);
1020
        if (r < 0)
16✔
1021
                return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
×
1022
        if (r == 0) {
16✔
1023
                errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
8✔
1024

1025
                _cleanup_close_ int container_fd = -EBADF;
×
1026
                container_fd = open_parent(container_path, O_CLOEXEC, 0);
8✔
1027
                if (container_fd < 0) {
8✔
1028
                        log_debug_errno(container_fd, "Failed to open container directory: %m");
×
1029
                        report_errno_and_exit(errno_pipe_fd[1], container_fd);
×
1030
                }
1031

1032
                /* Rejoin the host namespace, so that /proc/self/fd/… works, which copy_tree_at() relies on
1033
                 * in some cases (by means of fd_reopen()) */
1034
                if (setns(source_mntns_fd, CLONE_NEWNS) < 0) {
8✔
1035
                        r = log_debug_errno(errno, "Failed to rejoin namespace of host: %m");
×
1036
                        report_errno_and_exit(errno_pipe_fd[1], r);
×
1037
                }
1038

1039
                /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to
1040
                 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1041
                 * the UID/GIDs as they are. */
1042
                if (copy_from_container)
8✔
1043
                        r = copy_tree_at(
2✔
1044
                                        container_fd,
1045
                                        container_basename,
1046
                                        host_fd,
1047
                                        host_basename,
1048
                                        uid_shift == 0 ? UID_INVALID : 0,
1049
                                        uid_shift == 0 ? GID_INVALID : 0,
2✔
1050
                                        copy_flags,
1051
                                        /* denylist = */ NULL,
1052
                                        /* subvolumes = */ NULL);
1053
                else
1054
                        r = copy_tree_at(
6✔
1055
                                        host_fd,
1056
                                        host_basename,
1057
                                        container_fd,
1058
                                        container_basename,
1059
                                        uid_shift == 0 ? UID_INVALID : uid_shift,
1060
                                        uid_shift == 0 ? GID_INVALID : uid_shift,
6✔
1061
                                        copy_flags,
1062
                                        /* denylist = */ NULL,
1063
                                        /* subvolumes = */ NULL);
1064
                if (r < 0)
8✔
1065
                        log_debug_errno(r, "Failed to copy tree: %m");
1✔
1066

1067
                report_errno_and_exit(errno_pipe_fd[1], r);
8✔
1068
        }
1069

1070
        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
8✔
1071

1072
        Operation *operation;
8✔
1073
        r = operation_new(manager, machine, child, errno_pipe_fd[0], &operation);
8✔
1074
        if (r < 0)
8✔
1075
                return r;
1076

1077
        TAKE_FD(errno_pipe_fd[0]);
8✔
1078
        TAKE_PID(child);
8✔
1079

1080
        *ret = operation;
8✔
1081
        return 0;
8✔
1082
}
1083

1084
void machine_release_unit(Machine *m) {
91✔
1085
        assert(m);
91✔
1086

1087
        if (!m->unit)
91✔
1088
                return;
1089

1090
        assert(m->manager);
91✔
1091

1092
        if (m->referenced) {
91✔
1093
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1094
                int r;
66✔
1095

1096
                r = manager_unref_unit(m->manager, m->unit, &error);
66✔
1097
                if (r < 0)
66✔
1098
                        log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
×
1099
                                       "Failed to drop reference to machine scope, ignoring: %s",
1100
                                       bus_error_message(&error, r));
1101

1102
                m->referenced = false;
66✔
1103
        }
1104

1105
        (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
91✔
1106
        m->unit = mfree(m->unit);
91✔
1107
}
1108

1109
int machine_get_uid_shift(Machine *m, uid_t *ret) {
41✔
1110
        char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
41✔
1111
        uid_t uid_base, uid_shift, uid_range;
41✔
1112
        gid_t gid_base, gid_shift, gid_range;
41✔
1113
        _cleanup_fclose_ FILE *f = NULL;
41✔
1114
        int r;
41✔
1115

1116
        assert(m);
41✔
1117
        assert(ret);
41✔
1118

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

1125
        if (m->class == MACHINE_HOST) {
41✔
1126
                *ret = 0;
4✔
1127
                return 0;
4✔
1128
        }
1129

1130
        if (m->class != MACHINE_CONTAINER)
37✔
1131
                return -EOPNOTSUPP;
1132

1133
        xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader.pid);
37✔
1134
        f = fopen(p, "re");
37✔
1135
        if (!f) {
37✔
1136
                if (errno == ENOENT) {
×
1137
                        /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
1138
                        *ret = 0;
×
1139
                        return 0;
×
1140
                }
1141

1142
                return -errno;
×
1143
        }
1144

1145
        /* Read the first line. There's at least one. */
1146
        r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
37✔
1147
        if (r < 0)
37✔
1148
                return r;
1149

1150
        /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
1151
        if (uid_base != 0)
37✔
1152
                return -ENXIO;
1153
        /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
1154
        if (uid_range < UID_NOBODY)
37✔
1155
                return -ENXIO;
1156

1157
        /* If there's more than one line, then we don't support this mapping. */
1158
        r = safe_fgetc(f, NULL);
37✔
1159
        if (r < 0)
37✔
1160
                return r;
1161
        if (r != 0) /* Insist on EOF */
37✔
1162
                return -ENXIO;
1163

1164
        fclose(f);
37✔
1165

1166
        xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
37✔
1167
        f = fopen(p, "re");
37✔
1168
        if (!f)
37✔
1169
                return -errno;
×
1170

1171
        /* Read the first line. There's at least one. */
1172
        errno = 0;
37✔
1173
        r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
37✔
1174
        if (r == EOF)
37✔
1175
                return errno_or_else(ENOMSG);
×
1176
        assert(r >= 0);
37✔
1177
        if (r != 3)
37✔
1178
                return -EBADMSG;
1179

1180
        /* If there's more than one line, then we don't support this file. */
1181
        r = safe_fgetc(f, NULL);
37✔
1182
        if (r < 0)
37✔
1183
                return r;
1184
        if (r != 0) /* Insist on EOF */
37✔
1185
                return -ENXIO;
1186

1187
        /* If the UID and GID mapping doesn't match, we don't support this mapping. */
1188
        if (uid_base != (uid_t) gid_base)
37✔
1189
                return -ENXIO;
1190
        if (uid_shift != (uid_t) gid_shift)
37✔
1191
                return -ENXIO;
1192
        if (uid_range != (uid_t) gid_range)
37✔
1193
                return -ENXIO;
1194

1195
        *ret = uid_shift;
37✔
1196
        return 0;
37✔
1197
}
1198

1199
static int machine_owns_uid_internal(
232✔
1200
                Machine *machine,
1201
                const char *map_file, /* "uid_map" or "gid_map" */
1202
                uid_t uid,
1203
                uid_t *ret_internal_uid) {
1204

1205
        _cleanup_fclose_ FILE *f = NULL;
232✔
1206
        const char *p;
232✔
1207
        int r;
232✔
1208

1209
        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
1210
        assert_cc(sizeof(uid_t) == sizeof(gid_t));
232✔
1211

1212
        assert(machine);
232✔
1213

1214
        /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
1215
         * internally in the machine */
1216

1217
        if (machine->class != MACHINE_CONTAINER)
232✔
1218
                goto negative;
198✔
1219

1220
        p = procfs_file_alloca(machine->leader.pid, map_file);
34✔
1221
        f = fopen(p, "re");
34✔
1222
        if (!f) {
34✔
1223
                log_debug_errno(errno, "Failed to open %s, ignoring.", p);
×
1224
                goto negative;
×
1225
        }
1226

1227
        for (;;) {
98✔
1228
                uid_t uid_base, uid_shift, uid_range, converted;
66✔
1229

1230
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
66✔
1231
                if (r == -ENOMSG)
66✔
1232
                        break;
1233
                if (r < 0)
34✔
1234
                        return r;
2✔
1235

1236
                /* The private user namespace is disabled, ignoring. */
1237
                if (uid_shift == 0)
34✔
1238
                        continue;
32✔
1239

1240
                if (uid < uid_shift || uid >= uid_shift + uid_range)
34✔
1241
                        continue;
32✔
1242

1243
                converted = (uid - uid_shift + uid_base);
2✔
1244
                if (!uid_is_valid(converted))
2✔
1245
                        return -EINVAL;
1246

1247
                if (ret_internal_uid)
2✔
1248
                        *ret_internal_uid = converted;
2✔
1249

1250
                return true;
1251
        }
1252

1253
negative:
230✔
1254
        if (ret_internal_uid)
230✔
1255
                *ret_internal_uid = UID_INVALID;
230✔
1256

1257
        return false;
1258
}
1259

1260
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
117✔
1261
        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
117✔
1262
}
1263

1264
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
115✔
1265
        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
115✔
1266
}
1267

1268
static int machine_translate_uid_internal(
4✔
1269
                Machine *machine,
1270
                const char *map_file, /* "uid_map" or "gid_map" */
1271
                uid_t uid,
1272
                uid_t *ret_host_uid) {
1273

1274
        _cleanup_fclose_ FILE *f = NULL;
4✔
1275
        const char *p;
4✔
1276
        int r;
4✔
1277

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

1281
        assert(machine);
4✔
1282
        assert(uid_is_valid(uid));
4✔
1283

1284
        if (machine->class != MACHINE_CONTAINER)
4✔
1285
                return -ESRCH;
1286

1287
        /* Translates a machine UID into a host UID */
1288

1289
        p = procfs_file_alloca(machine->leader.pid, map_file);
4✔
1290
        f = fopen(p, "re");
4✔
1291
        if (!f)
4✔
1292
                return -errno;
×
1293

1294
        for (;;) {
4✔
1295
                uid_t uid_base, uid_shift, uid_range, converted;
4✔
1296

1297
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
4✔
1298
                if (r == -ENOMSG)
4✔
1299
                        break;
1300
                if (r < 0)
4✔
1301
                        return r;
4✔
1302

1303
                if (uid < uid_base || uid >= uid_base + uid_range)
4✔
1304
                        continue;
×
1305

1306
                converted = uid - uid_base + uid_shift;
4✔
1307
                if (!uid_is_valid(converted))
4✔
1308
                        return -EINVAL;
1309

1310
                if (ret_host_uid)
4✔
1311
                        *ret_host_uid = converted;
4✔
1312

1313
                return 0;
1314
        }
1315

1316
        return -ESRCH;
×
1317
}
1318

1319
int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
2✔
1320
        return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
2✔
1321
}
1322

1323
int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
2✔
1324
        return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
2✔
1325
}
1326

1327
int machine_open_root_directory(Machine *machine) {
3✔
1328
        int r;
3✔
1329

1330
        assert(machine);
3✔
1331

1332
        switch (machine->class) {
3✔
1333
        case MACHINE_HOST: {
2✔
1334
                int fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
2✔
1335
                if (fd < 0)
2✔
1336
                        return log_debug_errno(errno, "Failed to open host root directory: %m");
×
1337

1338
                return fd;
1339
        }
1340

1341
        case MACHINE_CONTAINER: {
1✔
1342
                _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
1✔
1343
                _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
2✔
1344
                pid_t child;
1✔
1345

1346
                r = pidref_namespace_open(&machine->leader,
1✔
1347
                                          /* ret_pidns_fd = */ NULL,
1348
                                          &mntns_fd,
1349
                                          /* ret_netns_fd = */ NULL,
1350
                                          /* ret_userns_fd = */ NULL,
1351
                                          &root_fd);
1352
                if (r < 0)
1✔
1353
                        return log_debug_errno(r, "Failed to open mount namespace of machine '%s': %m", machine->name);
×
1354

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

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

1361
                r = namespace_fork(
1✔
1362
                                "(sd-openrootns)",
1363
                                "(sd-openroot)",
1364
                                /* except_fds = */ NULL,
1365
                                /* n_except_fds = */ 0,
1366
                                FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
1367
                                /* pidns_fd = */  -EBADF,
1368
                                mntns_fd,
1369
                                /* netns_fd = */  -EBADF,
1370
                                /* userns_fd = */ -EBADF,
1371
                                root_fd,
1372
                                &child);
1373
                if (r < 0)
2✔
1374
                        return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
×
1375
                if (r == 0) {
2✔
1376
                        _cleanup_close_ int dfd = -EBADF;
×
1377

1378
                        errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1✔
1379
                        fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
1✔
1380

1381
                        dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1✔
1382
                        if (dfd < 0) {
1✔
1383
                                log_debug_errno(errno, "Failed to open root directory of machine '%s': %m", machine->name);
×
1384
                                report_errno_and_exit(errno_pipe_fd[1], -errno);
×
1385
                        }
1386

1387
                        r = send_one_fd(fd_pass_socket[1], dfd, /* flags = */ 0);
1✔
1388
                        dfd = safe_close(dfd);
1✔
1389
                        if (r < 0) {
1✔
1390
                                log_debug_errno(r, "Failed to send FD over socket: %m");
×
1391
                                report_errno_and_exit(errno_pipe_fd[1], r);
×
1392
                        }
1393

1394
                        _exit(EXIT_SUCCESS);
1✔
1395
                }
1396

1397
                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1✔
1398
                fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
1✔
1399

1400
                r = wait_for_terminate_and_check("(sd-openrootns)", child, /* flags = */ 0);
1✔
1401
                if (r < 0)
1✔
1402
                        return log_debug_errno(r, "Failed to wait for child: %m");
×
1403

1404
                r = read_errno(errno_pipe_fd[0]); /* the function does debug reporting */
1✔
1405
                if (r < 0)
1✔
1406
                        return r;
1407

1408
                int fd = receive_one_fd(fd_pass_socket[0], MSG_DONTWAIT);
1✔
1409
                if (fd < 0)
1✔
1410
                        return log_debug_errno(fd, "Failed to receive FD from child: %m");
×
1411

1412
                return fd;
1413
        }
1414

1415
        default:
1416
                return -EOPNOTSUPP;
1417
        }
1418
}
1419

1420
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
1421
        [MACHINE_CONTAINER] = "container",
1422
        [MACHINE_VM] = "vm",
1423
        [MACHINE_HOST] = "host",
1424
};
1425

1426
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
482✔
1427

1428
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
1429
        [MACHINE_OPENING] = "opening",
1430
        [MACHINE_RUNNING] = "running",
1431
        [MACHINE_CLOSING] = "closing"
1432
};
1433

1434
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
83✔
1435

1436
static const char* const kill_whom_table[_KILL_WHOM_MAX] = {
1437
        [KILL_LEADER] = "leader",
1438
        [KILL_ALL] = "all"
1439
};
1440

1441
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
70✔
1442

1443
static const char* const acquire_metadata_table[_ACQUIRE_METADATA_MAX] = {
1444
        [ACQUIRE_METADATA_NO]       = "no",
1445
        [ACQUIRE_METADATA_YES]      = "yes",
1446
        [ACQUIRE_METADATA_GRACEFUL] = "graceful"
1447
};
1448

1449
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