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

systemd / systemd / 15288324789

27 May 2025 07:40PM UTC coverage: 71.981% (-0.07%) from 72.046%
15288324789

push

github

yuwata
timedate: print better errors when systemd-timesyncd.service unavailable

If the error is a common bus error indicating the service is not
available, print a more user-friendly message indicating so.

0 of 7 new or added lines in 1 file covered. (0.0%)

3467 existing lines in 62 files now uncovered.

299170 of 415625 relevant lines covered (71.98%)

704053.27 hits per line

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

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

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

7
#include "sd-bus.h"
8
#include "sd-event.h"
9
#include "sd-messages.h"
10

11
#include "alloc-util.h"
12
#include "bus-error.h"
13
#include "bus-internal.h"
14
#include "bus-locator.h"
15
#include "bus-unit-util.h"
16
#include "env-file.h"
17
#include "errno-util.h"
18
#include "escape.h"
19
#include "extract-word.h"
20
#include "fd-util.h"
21
#include "fileio.h"
22
#include "format-util.h"
23
#include "fs-util.h"
24
#include "hashmap.h"
25
#include "log.h"
26
#include "machine.h"
27
#include "machine-dbus.h"
28
#include "machined.h"
29
#include "mkdir-label.h"
30
#include "namespace-util.h"
31
#include "operation.h"
32
#include "parse-util.h"
33
#include "path-util.h"
34
#include "process-util.h"
35
#include "serialize.h"
36
#include "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) {
92✔
49
        _cleanup_(machine_freep) Machine *m = NULL;
92✔
50

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

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

68
        if (name) {
92✔
69
                m->name = strdup(name);
90✔
70
                if (!m->name)
90✔
71
                        return -ENOMEM;
72
        }
73

74
        *ret = TAKE_PTR(m);
92✔
75
        return 0;
92✔
76
}
77

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

81
        assert(manager);
92✔
82
        assert(machine);
92✔
83

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

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

94
                free_and_replace(machine->state_file, temp);
86✔
95
        }
96

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

101
        machine->manager = manager;
92✔
102

103
        return 0;
92✔
104
}
105

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

110
        while (m->operations)
92✔
111
                operation_free(m->operations);
×
112

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

118
        if (m->manager) {
92✔
119
                machine_release_unit(m);
92✔
120

121
                (void) hashmap_remove(m->manager->machines, m->name);
92✔
122

123
                if (m->manager->host_machine == m)
92✔
124
                        m->manager->host_machine = NULL;
6✔
125
        }
126

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

134
        sd_bus_message_unref(m->create_message);
92✔
135

136
        free(m->name);
92✔
137
        free(m->scope_job);
92✔
138
        free(m->state_file);
92✔
139
        free(m->service);
92✔
140
        free(m->root_directory);
92✔
141
        free(m->netif);
92✔
142
        free(m->ssh_address);
92✔
143
        free(m->ssh_private_key_path);
92✔
144
        return mfree(m);
92✔
145
}
146

147
int machine_save(Machine *m) {
276✔
148
        int r;
276✔
149

150
        assert(m);
276✔
151

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

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

158
        _cleanup_(unlink_and_freep) char *sl = NULL; /* auto-unlink! */
276✔
159
        if (m->unit) {
276✔
160
                sl = strjoin("/run/systemd/machines/unit:", m->unit);
276✔
161
                if (!sl)
276✔
162
                        return log_oom();
×
163
        }
164

165
        r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
276✔
166
        if (r < 0)
276✔
UNCOV
167
                return log_error_errno(r, "Failed to create /run/systemd/machines/: %m");
×
168

UNCOV
169
        _cleanup_(unlink_and_freep) char *temp_path = NULL;
×
170
        _cleanup_fclose_ FILE *f = NULL;
276✔
171
        r = fopen_tmpfile_linkable(m->state_file, O_WRONLY|O_CLOEXEC, &temp_path, &f);
276✔
172
        if (r < 0)
276✔
UNCOV
173
                return log_error_errno(r, "Failed to create state file '%s': %m", m->state_file);
×
174

175
        if (fchmod(fileno(f), 0644) < 0)
276✔
UNCOV
176
                return log_error_errno(errno, "Failed to set access mode for state file '%s' to 0644: %m", m->state_file);
×
177

178
        fprintf(f,
276✔
179
                "# This is private data. Do not parse.\n"
180
                "NAME=%s\n",
181
                m->name);
182

183
        /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
184
        env_file_fputs_assignment(f, "SCOPE=", m->unit);
276✔
185
        env_file_fputs_assignment(f, "SCOPE_JOB=", m->scope_job);
276✔
186

187
        env_file_fputs_assignment(f, "SERVICE=", m->service);
276✔
188
        env_file_fputs_assignment(f, "ROOT=", m->root_directory);
276✔
189

190
        if (!sd_id128_is_null(m->id))
276✔
191
                fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
274✔
192

193
        if (pidref_is_set(&m->leader)) {
276✔
194
                fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
276✔
195
                (void) pidref_acquire_pidfd_id(&m->leader);
276✔
196
                if (m->leader.fd_id != 0)
276✔
197
                        fprintf(f, "LEADER_PIDFDID=%" PRIu64 "\n", m->leader.fd_id);
276✔
198
        }
199

200
        if (m->class != _MACHINE_CLASS_INVALID)
276✔
201
                fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
276✔
202

203
        if (dual_timestamp_is_set(&m->timestamp))
276✔
204
                fprintf(f,
276✔
205
                        "REALTIME="USEC_FMT"\n"
206
                        "MONOTONIC="USEC_FMT"\n",
207
                        m->timestamp.realtime,
208
                        m->timestamp.monotonic);
209

210
        if (m->n_netif > 0) {
276✔
211
                fputs("NETIF=\"", f);
48✔
212
                FOREACH_ARRAY(ifi, m->netif, m->n_netif) {
96✔
213
                        if (*ifi != 0)
48✔
214
                                fputc(' ', f);
48✔
215
                        fprintf(f, "%i", *ifi);
48✔
216
                }
217
                fputs("\"\n", f);
48✔
218
        }
219

220
        if (m->vsock_cid != 0)
276✔
221
                fprintf(f, "VSOCK_CID=%u\n", m->vsock_cid);
276✔
222

223
        env_file_fputs_assignment(f, "SSH_ADDRESS=", m->ssh_address);
276✔
224
        env_file_fputs_assignment(f, "SSH_PRIVATE_KEY_PATH=", m->ssh_private_key_path);
276✔
225

226
        r = flink_tmpfile(f, temp_path, m->state_file, LINK_TMPFILE_REPLACE);
276✔
227
        if (r < 0)
276✔
UNCOV
228
                return log_error_errno(r, "Failed to move '%s' into place: %m", m->state_file);
×
229

230
        temp_path = mfree(temp_path); /* disarm auto-destroy: temporary file does not exist anymore */
276✔
231

232
        if (sl) {
276✔
233
                /* Create a symlink from the unit name to the machine name, so that we can quickly find the machine
234
                 * for each given unit. Ignore error. */
235
                (void) symlink(m->name, sl);
276✔
236

237
                /* disarm auto-removal */
238
                sl = mfree(sl);
276✔
239
        }
240

241
        return 0;
242
}
243

244
static void machine_unlink(Machine *m) {
118✔
245
        assert(m);
118✔
246

247
        if (m->unit) {
118✔
248
                const char *sl = strjoina("/run/systemd/machines/unit:", m->unit);
590✔
249
                (void) unlink(sl);
118✔
250
        }
251

252
        if (m->state_file)
118✔
253
                (void) unlink(m->state_file);
118✔
254
}
118✔
255

UNCOV
256
int machine_load(Machine *m) {
×
UNCOV
257
        _cleanup_free_ char *name = NULL, *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *leader_pidfdid = NULL,
×
UNCOV
258
                *class = NULL, *netif = NULL, *vsock_cid = NULL;
×
UNCOV
259
        int r;
×
260

UNCOV
261
        assert(m);
×
262

UNCOV
263
        if (!m->state_file)
×
264
                return 0;
265

266
        r = parse_env_file(NULL, m->state_file,
×
267
                           "NAME",                 &name,
268
                           "SCOPE",                &m->unit,
269
                           "SCOPE_JOB",            &m->scope_job,
270
                           "SERVICE",              &m->service,
271
                           "ROOT",                 &m->root_directory,
272
                           "ID",                   &id,
273
                           "LEADER",               &leader,
274
                           "LEADER_PIDFDID",       &leader_pidfdid,
275
                           "CLASS",                &class,
276
                           "REALTIME",             &realtime,
277
                           "MONOTONIC",            &monotonic,
278
                           "NETIF",                &netif,
279
                           "VSOCK_CID",            &vsock_cid,
280
                           "SSH_ADDRESS",          &m->ssh_address,
281
                           "SSH_PRIVATE_KEY_PATH", &m->ssh_private_key_path);
UNCOV
282
        if (r == -ENOENT)
×
283
                return 0;
UNCOV
284
        if (r < 0)
×
UNCOV
285
                return log_error_errno(r, "Failed to read %s: %m", m->state_file);
×
286

287
        if (!streq_ptr(name, m->name))
×
288
                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);
×
289

290
        if (id)
×
UNCOV
291
                (void) sd_id128_from_string(id, &m->id);
×
292

UNCOV
293
        pidref_done(&m->leader);
×
UNCOV
294
        if (leader) {
×
295
                r = pidref_set_pidstr(&m->leader, leader);
×
UNCOV
296
                if (r < 0)
×
UNCOV
297
                        log_debug_errno(r, "Failed to set leader PID to '%s', ignoring: %m", leader);
×
UNCOV
298
                else if (leader_pidfdid) {
×
UNCOV
299
                        uint64_t fd_id;
×
UNCOV
300
                        r = safe_atou64(leader_pidfdid, &fd_id);
×
UNCOV
301
                        if (r < 0)
×
UNCOV
302
                                log_warning_errno(r, "Failed to parse leader pidfd ID, ignoring: %s", leader_pidfdid);
×
303
                        else {
UNCOV
304
                                (void) pidref_acquire_pidfd_id(&m->leader);
×
305

306
                                if (fd_id != m->leader.fd_id) {
×
UNCOV
307
                                        log_debug("Leader PID got recycled, ignoring.");
×
308
                                        pidref_done(&m->leader);
×
309
                                }
310
                        }
311
                }
312
        }
313

314
        if (class) {
×
315
                MachineClass c = machine_class_from_string(class);
×
316
                if (c >= 0)
×
317
                        m->class = c;
×
318
        }
319

UNCOV
320
        if (realtime)
×
321
                (void) deserialize_usec(realtime, &m->timestamp.realtime);
×
322
        if (monotonic)
×
UNCOV
323
                (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
×
324

325
        m->netif = mfree(m->netif);
×
326
        m->n_netif = 0;
×
UNCOV
327
        if (netif) {
×
UNCOV
328
                _cleanup_free_ int *ni = NULL;
×
329
                size_t nr = 0;
×
330

331
                for (const char *p = netif;;) {
×
332
                        _cleanup_free_ char *word = NULL;
×
333

334
                        r = extract_first_word(&p, &word, NULL, 0);
×
335
                        if (r == 0)
×
336
                                break;
337
                        if (r == -ENOMEM)
×
UNCOV
338
                                return log_oom();
×
339
                        if (r < 0) {
×
340
                                log_warning_errno(r, "Failed to parse NETIF: %s", netif);
×
341
                                break;
342
                        }
343

344
                        r = parse_ifindex(word);
×
UNCOV
345
                        if (r < 0)
×
346
                                continue;
×
347

348
                        if (!GREEDY_REALLOC(ni, nr + 1))
×
349
                                return log_oom();
×
350

UNCOV
351
                        ni[nr++] = r;
×
352
                }
353

354
                m->netif = TAKE_PTR(ni);
×
355
                m->n_netif = nr;
×
356
        }
357

358
        m->vsock_cid = 0;
×
UNCOV
359
        if (vsock_cid) {
×
360
                r = safe_atou(vsock_cid, &m->vsock_cid);
×
UNCOV
361
                if (r < 0)
×
UNCOV
362
                        log_warning_errno(r, "Failed to parse AF_VSOCK CID, ignoring: %s", vsock_cid);
×
363
        }
364

365
        return r;
366
}
367

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

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

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

384
        escaped = unit_name_escape(machine->name);
67✔
385
        if (!escaped)
67✔
UNCOV
386
                return log_oom();
×
387

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

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

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

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

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

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

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

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

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

435
        r = sd_bus_message_close_container(m);
67✔
436
        if (r < 0)
67✔
437
                return r;
438

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

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

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

454
        machine->unit = TAKE_PTR(unit);
67✔
455
        machine->referenced = true;
67✔
456

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

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

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

468
        assert(m);
86✔
469
        assert(m->class != MACHINE_HOST);
86✔
470

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

477
        assert(m->unit);
86✔
478

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

483
        return 0;
484
}
485

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

489
        m->leader_pidfd_event_source = sd_event_source_disable_unref(m->leader_pidfd_event_source);
86✔
490
        machine_add_to_gc_queue(m);
86✔
491

492
        return 0;
86✔
493
}
494

495
static int machine_watch_pidfd(Machine *m) {
86✔
496
        int r;
86✔
497

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

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

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

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

513
        (void) sd_event_source_set_description(m->leader_pidfd_event_source, "machine-pidfd");
86✔
514

515
        return 0;
86✔
516
}
517

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

521
        assert(m);
92✔
522

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

526
        if (m->started)
86✔
527
                return 0;
528

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

533
        r = machine_watch_pidfd(m);
86✔
534
        if (r < 0)
86✔
535
                return r;
536

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

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

548
        if (!dual_timestamp_is_set(&m->timestamp))
86✔
549
                dual_timestamp_now(&m->timestamp);
86✔
550

551
        m->started = true;
86✔
552

553
        /* Save new machine data */
554
        machine_save(m);
86✔
555

556
        machine_send_signal(m, true);
86✔
557

558
        return 0;
86✔
559
}
560

561
int machine_stop(Machine *m) {
56✔
562
        int r;
56✔
563

564
        assert(m);
56✔
565

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

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

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

577
                free_and_replace(m->scope_job, job);
56✔
578
        }
579

580
        m->stopping = true;
56✔
581

582
        machine_save(m);
56✔
583

584
        return 0;
56✔
585
}
586

587
int machine_finalize(Machine *m) {
118✔
588
        assert(m);
118✔
589

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

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

600
        machine_unlink(m);
118✔
601
        machine_add_to_gc_queue(m);
118✔
602

603
        if (m->started) {
118✔
604
                machine_send_signal(m, false);
83✔
605
                m->started = false;
83✔
606
        }
607

608
        return 0;
118✔
609
}
610

611
bool machine_may_gc(Machine *m, bool drop_not_started) {
602✔
612
        int r;
602✔
613

614
        assert(m);
602✔
615

616
        if (m->class == MACHINE_HOST)
602✔
617
                return false;
618

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

622
        r = pidref_is_alive(&m->leader);
512✔
623
        if (r == -ESRCH)
512✔
624
                return true;
625
        if (r < 0)
512✔
UNCOV
626
                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", m->leader.pid);
×
627
        if (r > 0)
512✔
628
                return false;
629

630
        if (m->scope_job) {
166✔
631
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
45✔
632

633
                r = manager_job_is_active(m->manager, m->scope_job, &error);
60✔
634
                if (r < 0)
60✔
UNCOV
635
                        log_debug_errno(r, "Failed to determine whether job '%s' is active, assuming it is: %s", m->scope_job, bus_error_message(&error, r));
×
636
                if (r != 0)
60✔
637
                        return false;
15✔
638
        }
639

640
        if (m->unit) {
151✔
641
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
100✔
642

643
                r = manager_unit_is_active(m->manager, m->unit, &error);
151✔
644
                if (r < 0)
151✔
UNCOV
645
                        log_debug_errno(r, "Failed to determine whether unit '%s' is active, assuming it is: %s", m->unit, bus_error_message(&error, r));
×
646
                if (r != 0)
151✔
647
                        return false;
51✔
648
        }
649

650
        return true;
651
}
652

653
void machine_add_to_gc_queue(Machine *m) {
924✔
654
        assert(m);
924✔
655

656
        if (m->in_gc_queue)
924✔
657
                return;
658

659
        LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
384✔
660
        m->in_gc_queue = true;
384✔
661

662
        manager_enqueue_gc(m->manager);
384✔
663
}
664

665
MachineState machine_get_state(Machine *s) {
118✔
666
        assert(s);
118✔
667

668
        if (s->class == MACHINE_HOST)
118✔
669
                return MACHINE_RUNNING;
670

671
        if (s->stopping)
118✔
672
                return MACHINE_CLOSING;
673

674
        if (s->scope_job)
83✔
UNCOV
675
                return MACHINE_OPENING;
×
676

677
        return MACHINE_RUNNING;
678
}
679

680
int machine_kill(Machine *m, KillWhom whom, int signo) {
21✔
681
        assert(m);
21✔
682

683
        if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
21✔
684
                return -EOPNOTSUPP;
685

686
        if (!m->unit)
21✔
687
                return -ESRCH;
688

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

692
        /* Otherwise, make PID 1 do it for us, for the entire cgroup */
693
        return manager_kill_unit(m->manager, m->unit, signo, NULL);
4✔
694
}
695

696
int machine_openpt(Machine *m, int flags, char **ret_peer) {
8✔
697
        assert(m);
8✔
698

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

701
        case MACHINE_HOST:
8✔
702
                return openpt_allocate(flags, ret_peer);
8✔
703

704
        case MACHINE_CONTAINER:
UNCOV
705
                if (!pidref_is_set(&m->leader))
×
706
                        return -EINVAL;
707

708
                return openpt_allocate_in_namespace(&m->leader, flags, ret_peer);
×
709

710
        default:
711
                return -EOPNOTSUPP;
712
        }
713
}
714

715
static int machine_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
6✔
716
        int r;
6✔
717

718
        assert(m);
6✔
719
        assert(ret);
6✔
720

721
        switch (m->class) {
6✔
722

723
        case MACHINE_HOST:
6✔
724
                *ret = NULL;
6✔
725
                return 0;
6✔
726

727
        case MACHINE_CONTAINER: {
×
UNCOV
728
                _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
×
UNCOV
729
                char *address;
×
730

731
                r = sd_bus_new(&bus);
×
UNCOV
732
                if (r < 0)
×
UNCOV
733
                        return log_debug_errno(r, "Failed to allocate new DBus: %m");
×
734

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

UNCOV
738
                bus->address = address;
×
UNCOV
739
                bus->bus_client = true;
×
UNCOV
740
                bus->trusted = false;
×
UNCOV
741
                bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
×
742

UNCOV
743
                r = sd_bus_start(bus);
×
UNCOV
744
                if (r == -ENOENT)
×
UNCOV
745
                        return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
×
UNCOV
746
                if (r < 0)
×
747
                        return r;
748

UNCOV
749
                *ret = TAKE_PTR(bus);
×
750
                return 0;
×
751
        }
752

753
        default:
754
                return -EOPNOTSUPP;
755
        }
756
}
757

758
int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error) {
1✔
759
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
1✔
760
        sd_bus *container_bus = NULL;
1✔
761
        const char *p, *getty;
1✔
762
        int r;
1✔
763

764
        assert(m);
1✔
765
        assert(ptmx_name);
1✔
766

767
        p = path_startswith(ptmx_name, "/dev/pts/");
1✔
768
        if (!p)
1✔
UNCOV
769
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
770

771
        r = machine_bus_new(m, error, &allocated_bus);
1✔
772
        if (r < 0)
1✔
UNCOV
773
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
774

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

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

782
        return 0;
783
}
784

785
int machine_start_shell(
5✔
786
                Machine *m,
787
                int ptmx_fd,
788
                const char *ptmx_name,
789
                const char *user,
790
                const char *path,
791
                char **args,
792
                char **env,
793
                sd_bus_error *error) {
794
        _cleanup_close_ int pty_fd = -EBADF;
5✔
795
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *tm = NULL;
5✔
796
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
5✔
797
        const char *p, *utmp_id, *unit, *description;
5✔
798
        sd_bus *container_bus = NULL;
5✔
799
        int r;
5✔
800

801
        assert(m);
5✔
802
        assert(ptmx_fd >= 0);
5✔
803
        assert(ptmx_name);
5✔
804

805
        if (isempty(user) || isempty(path) || strv_isempty(args))
20✔
806
                return -EINVAL;
807

808
        p = path_startswith(ptmx_name, "/dev/pts/");
5✔
809
        utmp_id = path_startswith(ptmx_name, "/dev/");
5✔
810
        if (!p || !utmp_id)
5✔
UNCOV
811
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
×
812

813
        pty_fd = pty_open_peer(ptmx_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
5✔
814
        if (pty_fd < 0)
5✔
UNCOV
815
                return log_debug_errno(pty_fd, "Failed to open terminal: %m");
×
816

817
        r = machine_bus_new(m, error, &allocated_bus);
5✔
818
        if (r < 0)
5✔
UNCOV
819
                return log_debug_errno(r, "Failed to create DBus to machine: %m");
×
820

821
        container_bus = allocated_bus ?: m->manager->bus;
5✔
822
        r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
5✔
823
        if (r < 0)
5✔
824
                return r;
825

826
        /* Name and mode */
827
        unit = strjoina("container-shell@", p, ".service");
35✔
828
        r = sd_bus_message_append(tm, "ss", unit, "fail");
5✔
829
        if (r < 0)
5✔
830
                return r;
831

832
        /* Properties */
833
        r = sd_bus_message_open_container(tm, 'a', "(sv)");
5✔
834
        if (r < 0)
5✔
835
                return r;
836

837
        description = strjoina("Shell for User ", user);
25✔
838
        r = sd_bus_message_append(tm,
5✔
839
                                  "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
840
                                  "Description", "s", description,
841
                                  "StandardInputFileDescriptor", "h", pty_fd,
842
                                  "StandardOutputFileDescriptor", "h", pty_fd,
843
                                  "StandardErrorFileDescriptor", "h", pty_fd,
844
                                  "SendSIGHUP", "b", true,
845
                                  "IgnoreSIGPIPE", "b", false,
846
                                  "KillMode", "s", "mixed",
847
                                  "TTYPath", "s", ptmx_name,
848
                                  "TTYReset", "b", true,
849
                                  "UtmpIdentifier", "s", utmp_id,
850
                                  "UtmpMode", "s", "user",
851
                                  "PAMName", "s", "login",
852
                                  "WorkingDirectory", "s", "-~");
853
        if (r < 0)
5✔
854
                return r;
855

856
        r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
5✔
857
        if (r < 0)
5✔
858
                return r;
859

860
        if (!strv_isempty(env)) {
5✔
861
                r = sd_bus_message_open_container(tm, 'r', "sv");
4✔
862
                if (r < 0)
4✔
863
                        return r;
864

865
                r = sd_bus_message_append(tm, "s", "Environment");
4✔
866
                if (r < 0)
4✔
867
                        return r;
868

869
                r = sd_bus_message_open_container(tm, 'v', "as");
4✔
870
                if (r < 0)
4✔
871
                        return r;
872

873
                r = sd_bus_message_append_strv(tm, env);
4✔
874
                if (r < 0)
4✔
875
                        return r;
876

877
                r = sd_bus_message_close_container(tm);
4✔
878
                if (r < 0)
4✔
879
                        return r;
880

881
                r = sd_bus_message_close_container(tm);
4✔
882
                if (r < 0)
4✔
883
                        return r;
884
        }
885

886
        /* Exec container */
887
        r = sd_bus_message_open_container(tm, 'r', "sv");
5✔
888
        if (r < 0)
5✔
889
                return r;
890

891
        r = sd_bus_message_append(tm, "s", "ExecStart");
5✔
892
        if (r < 0)
5✔
893
                return r;
894

895
        r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
5✔
896
        if (r < 0)
5✔
897
                return r;
898

899
        r = sd_bus_message_open_container(tm, 'a', "(sasb)");
5✔
900
        if (r < 0)
5✔
901
                return r;
902

903
        r = sd_bus_message_open_container(tm, 'r', "sasb");
5✔
904
        if (r < 0)
5✔
905
                return r;
906

907
        r = sd_bus_message_append(tm, "s", path);
5✔
908
        if (r < 0)
5✔
909
                return r;
910

911
        r = sd_bus_message_append_strv(tm, args);
5✔
912
        if (r < 0)
5✔
913
                return r;
914

915
        r = sd_bus_message_append(tm, "b", true);
5✔
916
        if (r < 0)
5✔
917
                return r;
918

919
        r = sd_bus_message_close_container(tm);
5✔
920
        if (r < 0)
5✔
921
                return r;
922

923
        r = sd_bus_message_close_container(tm);
5✔
924
        if (r < 0)
5✔
925
                return r;
926

927
        r = sd_bus_message_close_container(tm);
5✔
928
        if (r < 0)
5✔
929
                return r;
930

931
        r = sd_bus_message_close_container(tm);
5✔
932
        if (r < 0)
5✔
933
                return r;
934

935
        r = sd_bus_message_close_container(tm);
5✔
936
        if (r < 0)
5✔
937
                return r;
938

939
        /* Auxiliary units */
940
        r = sd_bus_message_append(tm, "a(sa(sv))", 0);
5✔
941
        if (r < 0)
5✔
942
                return r;
943

944
        r = sd_bus_call(container_bus, tm, 0, error, NULL);
5✔
945
        if (r < 0)
5✔
UNCOV
946
                return r;
×
947

948
        return 0;
949
}
950

951
char** machine_default_shell_args(const char *user) {
1✔
952
        _cleanup_strv_free_ char **args = NULL;
1✔
953
        int r;
1✔
954

955
        assert(user);
1✔
956

957
        args = new0(char*, 3 + 1);
1✔
958
        if (!args)
1✔
959
                return NULL;
960

961
        args[0] = strdup("sh");
1✔
962
        if (!args[0])
1✔
963
                return NULL;
964

965
        args[1] = strdup("-c");
1✔
966
        if (!args[1])
1✔
967
                return NULL;
968

969
        r = asprintf(&args[2],
1✔
970
                     "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
971
                     "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
972
                     user);
973
        if (r < 0) {
1✔
974
                args[2] = NULL;
×
UNCOV
975
                return NULL;
×
976
        }
977

978
        return TAKE_PTR(args);
1✔
979
}
980

981
int machine_copy_from_to_operation(
8✔
982
                Manager *manager,
983
                Machine *machine,
984
                const char *host_path,
985
                const char *container_path,
986
                bool copy_from_container,
987
                CopyFlags copy_flags,
988
                Operation **ret) {
989

990
        _cleanup_close_ int host_fd = -EBADF, target_mntns_fd = -EBADF, source_mntns_fd = -EBADF;
16✔
991
        _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
8✔
992
        _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
8✔
UNCOV
993
        _cleanup_(sigkill_waitp) pid_t child = 0;
×
994
        uid_t uid_shift;
8✔
995
        int r;
8✔
996

997
        assert(manager);
8✔
998
        assert(machine);
8✔
999
        assert(ret);
8✔
1000

1001
        if (isempty(host_path) || isempty(container_path))
24✔
1002
                return -EINVAL;
1003

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

1008
        r = path_extract_filename(container_path, &container_basename);
8✔
1009
        if (r < 0)
8✔
1010
                return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path);
×
1011

1012
        host_fd = open_parent(host_path, O_CLOEXEC, 0);
8✔
1013
        if (host_fd < 0)
8✔
UNCOV
1014
                return log_debug_errno(host_fd, "Failed to open host directory '%s': %m", host_path);
×
1015

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

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

1024
        source_mntns_fd = namespace_open_by_type(NAMESPACE_MOUNT);
8✔
1025
        if (source_mntns_fd < 0)
8✔
UNCOV
1026
                return log_debug_errno(source_mntns_fd, "Failed to open our own mount namespace: %m");
×
1027

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

1031
        r = namespace_fork("(sd-copyns)",
8✔
1032
                           "(sd-copy)",
1033
                           /* except_fds = */ NULL,
1034
                           /* n_except_fds = */ 0,
1035
                           FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
1036
                           /* pidns_fd = */ -EBADF,
1037
                           target_mntns_fd,
1038
                           /* netns_fd = */ -EBADF,
1039
                           /* userns_fd = */ -EBADF,
1040
                           /* root_fd = */ -EBADF,
1041
                           &child);
1042
        if (r < 0)
16✔
UNCOV
1043
                return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
×
1044
        if (r == 0) {
16✔
1045
                errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
8✔
1046

UNCOV
1047
                _cleanup_close_ int container_fd = -EBADF;
×
1048
                container_fd = open_parent(container_path, O_CLOEXEC, 0);
8✔
1049
                if (container_fd < 0) {
8✔
UNCOV
1050
                        log_debug_errno(container_fd, "Failed to open container directory: %m");
×
UNCOV
1051
                        report_errno_and_exit(errno_pipe_fd[1], container_fd);
×
1052
                }
1053

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

1061
                /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to
1062
                 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1063
                 * the UID/GIDs as they are. */
1064
                if (copy_from_container)
8✔
1065
                        r = copy_tree_at(
2✔
1066
                                        container_fd,
1067
                                        container_basename,
1068
                                        host_fd,
1069
                                        host_basename,
1070
                                        uid_shift == 0 ? UID_INVALID : 0,
1071
                                        uid_shift == 0 ? GID_INVALID : 0,
2✔
1072
                                        copy_flags,
1073
                                        /* denylist = */ NULL,
1074
                                        /* subvolumes = */ NULL);
1075
                else
1076
                        r = copy_tree_at(
6✔
1077
                                        host_fd,
1078
                                        host_basename,
1079
                                        container_fd,
1080
                                        container_basename,
1081
                                        uid_shift == 0 ? UID_INVALID : uid_shift,
1082
                                        uid_shift == 0 ? GID_INVALID : uid_shift,
6✔
1083
                                        copy_flags,
1084
                                        /* denylist = */ NULL,
1085
                                        /* subvolumes = */ NULL);
1086
                if (r < 0)
8✔
1087
                        log_debug_errno(r, "Failed to copy tree: %m");
1✔
1088

1089
                report_errno_and_exit(errno_pipe_fd[1], r);
8✔
1090
        }
1091

1092
        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
8✔
1093

1094
        Operation *operation;
8✔
1095
        r = operation_new(manager, machine, child, errno_pipe_fd[0], &operation);
8✔
1096
        if (r < 0)
8✔
1097
                return r;
1098

1099
        TAKE_FD(errno_pipe_fd[0]);
8✔
1100
        TAKE_PID(child);
8✔
1101

1102
        *ret = operation;
8✔
1103
        return 0;
8✔
1104
}
1105

1106
void machine_release_unit(Machine *m) {
92✔
1107
        assert(m);
92✔
1108

1109
        if (!m->unit)
92✔
1110
                return;
1111

1112
        assert(m->manager);
92✔
1113

1114
        if (m->referenced) {
92✔
UNCOV
1115
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1116
                int r;
67✔
1117

1118
                r = manager_unref_unit(m->manager, m->unit, &error);
67✔
1119
                if (r < 0)
67✔
UNCOV
1120
                        log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
×
1121
                                       "Failed to drop reference to machine scope, ignoring: %s",
1122
                                       bus_error_message(&error, r));
1123

1124
                m->referenced = false;
67✔
1125
        }
1126

1127
        (void) hashmap_remove_value(m->manager->machines_by_unit, m->unit, m);
92✔
1128
        m->unit = mfree(m->unit);
92✔
1129
}
1130

1131
int machine_get_uid_shift(Machine *m, uid_t *ret) {
42✔
1132
        char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
42✔
1133
        uid_t uid_base, uid_shift, uid_range;
42✔
1134
        gid_t gid_base, gid_shift, gid_range;
42✔
1135
        _cleanup_fclose_ FILE *f = NULL;
42✔
1136
        int r;
42✔
1137

1138
        assert(m);
42✔
1139
        assert(ret);
42✔
1140

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

1147
        if (m->class == MACHINE_HOST) {
42✔
1148
                *ret = 0;
5✔
1149
                return 0;
5✔
1150
        }
1151

1152
        if (m->class != MACHINE_CONTAINER)
37✔
1153
                return -EOPNOTSUPP;
1154

1155
        xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader.pid);
37✔
1156
        f = fopen(p, "re");
37✔
1157
        if (!f) {
37✔
UNCOV
1158
                if (errno == ENOENT) {
×
1159
                        /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
UNCOV
1160
                        *ret = 0;
×
UNCOV
1161
                        return 0;
×
1162
                }
1163

UNCOV
1164
                return -errno;
×
1165
        }
1166

1167
        /* Read the first line. There's at least one. */
1168
        r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
37✔
1169
        if (r < 0)
37✔
1170
                return r;
1171

1172
        /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
1173
        if (uid_base != 0)
37✔
1174
                return -ENXIO;
1175
        /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
1176
        if (uid_range < UID_NOBODY)
37✔
1177
                return -ENXIO;
1178

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

1186
        fclose(f);
37✔
1187

1188
        xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
37✔
1189
        f = fopen(p, "re");
37✔
1190
        if (!f)
37✔
UNCOV
1191
                return -errno;
×
1192

1193
        /* Read the first line. There's at least one. */
1194
        errno = 0;
37✔
1195
        r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
37✔
1196
        if (r == EOF)
37✔
UNCOV
1197
                return errno_or_else(ENOMSG);
×
1198
        assert(r >= 0);
37✔
1199
        if (r != 3)
37✔
1200
                return -EBADMSG;
1201

1202
        /* If there's more than one line, then we don't support this file. */
1203
        r = safe_fgetc(f, NULL);
37✔
1204
        if (r < 0)
37✔
1205
                return r;
1206
        if (r != 0) /* Insist on EOF */
37✔
1207
                return -ENXIO;
1208

1209
        /* If the UID and GID mapping doesn't match, we don't support this mapping. */
1210
        if (uid_base != (uid_t) gid_base)
37✔
1211
                return -ENXIO;
1212
        if (uid_shift != (uid_t) gid_shift)
37✔
1213
                return -ENXIO;
1214
        if (uid_range != (uid_t) gid_range)
37✔
1215
                return -ENXIO;
1216

1217
        r = pidref_verify(&m->leader);
37✔
1218
        if (r < 0)
37✔
1219
                return r;
1220

1221
        *ret = uid_shift;
37✔
1222
        return 0;
37✔
1223
}
1224

1225
static int machine_owns_uid_internal(
232✔
1226
                Machine *machine,
1227
                const char *map_file, /* "uid_map" or "gid_map" */
1228
                uid_t uid,
1229
                uid_t *ret_internal_uid) {
1230

1231
        _cleanup_fclose_ FILE *f = NULL;
232✔
1232
        const char *p;
232✔
1233
        int r;
232✔
1234

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

1238
        assert(machine);
232✔
1239

1240
        /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
1241
         * internally in the machine */
1242

1243
        if (machine->class != MACHINE_CONTAINER)
232✔
1244
                goto negative;
198✔
1245

1246
        p = procfs_file_alloca(machine->leader.pid, map_file);
34✔
1247
        f = fopen(p, "re");
34✔
1248
        if (!f) {
34✔
UNCOV
1249
                log_debug_errno(errno, "Failed to open %s, ignoring.", p);
×
UNCOV
1250
                goto negative;
×
1251
        }
1252

1253
        for (;;) {
98✔
1254
                uid_t uid_base, uid_shift, uid_range, converted;
66✔
1255

1256
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
66✔
1257
                if (r == -ENOMSG)
66✔
1258
                        break;
1259
                if (r < 0)
34✔
1260
                        return r;
2✔
1261

1262
                /* The private user namespace is disabled, ignoring. */
1263
                if (uid_shift == 0)
34✔
1264
                        continue;
32✔
1265

1266
                if (uid < uid_shift || uid >= uid_shift + uid_range)
34✔
1267
                        continue;
32✔
1268

1269
                converted = (uid - uid_shift + uid_base);
2✔
1270
                if (!uid_is_valid(converted))
2✔
1271
                        return -EINVAL;
1272

1273
                r = pidref_verify(&machine->leader);
2✔
1274
                if (r < 0)
2✔
1275
                        return r;
1276

1277
                if (ret_internal_uid)
2✔
1278
                        *ret_internal_uid = converted;
2✔
1279

1280
                return true;
1281
        }
1282

1283
negative:
230✔
1284
        if (ret_internal_uid)
230✔
1285
                *ret_internal_uid = UID_INVALID;
230✔
1286

1287
        return false;
1288
}
1289

1290
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
117✔
1291
        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
117✔
1292
}
1293

1294
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
115✔
1295
        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
115✔
1296
}
1297

1298
static int machine_translate_uid_internal(
4✔
1299
                Machine *machine,
1300
                const char *map_file, /* "uid_map" or "gid_map" */
1301
                uid_t uid,
1302
                uid_t *ret_host_uid) {
1303

1304
        _cleanup_fclose_ FILE *f = NULL;
4✔
1305
        const char *p;
4✔
1306
        int r;
4✔
1307

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

1311
        assert(machine);
4✔
1312
        assert(uid_is_valid(uid));
4✔
1313

1314
        if (machine->class != MACHINE_CONTAINER)
4✔
1315
                return -ESRCH;
1316

1317
        /* Translates a machine UID into a host UID */
1318

1319
        p = procfs_file_alloca(machine->leader.pid, map_file);
4✔
1320
        f = fopen(p, "re");
4✔
1321
        if (!f)
4✔
UNCOV
1322
                return -errno;
×
1323

1324
        for (;;) {
4✔
1325
                uid_t uid_base, uid_shift, uid_range, converted;
4✔
1326

1327
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
4✔
1328
                if (r == -ENOMSG)
4✔
1329
                        break;
1330
                if (r < 0)
4✔
1331
                        return r;
4✔
1332

1333
                if (uid < uid_base || uid >= uid_base + uid_range)
4✔
UNCOV
1334
                        continue;
×
1335

1336
                converted = uid - uid_base + uid_shift;
4✔
1337
                if (!uid_is_valid(converted))
4✔
1338
                        return -EINVAL;
1339

1340
                r = pidref_verify(&machine->leader);
4✔
1341
                if (r < 0)
4✔
1342
                        return r;
1343

1344
                if (ret_host_uid)
4✔
1345
                        *ret_host_uid = converted;
4✔
1346

1347
                return 0;
1348
        }
1349

UNCOV
1350
        return -ESRCH;
×
1351
}
1352

1353
int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
2✔
1354
        return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
2✔
1355
}
1356

1357
int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
2✔
1358
        return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
2✔
1359
}
1360

1361
int machine_open_root_directory(Machine *machine) {
3✔
1362
        int r;
3✔
1363

1364
        assert(machine);
3✔
1365

1366
        switch (machine->class) {
3✔
1367
        case MACHINE_HOST: {
2✔
1368
                int fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
2✔
1369
                if (fd < 0)
2✔
UNCOV
1370
                        return log_debug_errno(errno, "Failed to open host root directory: %m");
×
1371

1372
                return fd;
1373
        }
1374

1375
        case MACHINE_CONTAINER: {
1✔
1376
                _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
1✔
1377
                _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
2✔
1378
                pid_t child;
1✔
1379

1380
                r = pidref_namespace_open(&machine->leader,
1✔
1381
                                          /* ret_pidns_fd = */ NULL,
1382
                                          &mntns_fd,
1383
                                          /* ret_netns_fd = */ NULL,
1384
                                          /* ret_userns_fd = */ NULL,
1385
                                          &root_fd);
1386
                if (r < 0)
1✔
UNCOV
1387
                        return log_debug_errno(r, "Failed to open mount namespace of machine '%s': %m", machine->name);
×
1388

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

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

1395
                r = namespace_fork(
1✔
1396
                                "(sd-openrootns)",
1397
                                "(sd-openroot)",
1398
                                /* except_fds = */ NULL,
1399
                                /* n_except_fds = */ 0,
1400
                                FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
1401
                                /* pidns_fd = */  -EBADF,
1402
                                mntns_fd,
1403
                                /* netns_fd = */  -EBADF,
1404
                                /* userns_fd = */ -EBADF,
1405
                                root_fd,
1406
                                &child);
1407
                if (r < 0)
2✔
UNCOV
1408
                        return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
×
1409
                if (r == 0) {
2✔
UNCOV
1410
                        _cleanup_close_ int dfd = -EBADF;
×
1411

1412
                        errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1✔
1413
                        fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
1✔
1414

1415
                        dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1✔
1416
                        if (dfd < 0) {
1✔
1417
                                log_debug_errno(errno, "Failed to open root directory of machine '%s': %m", machine->name);
×
UNCOV
1418
                                report_errno_and_exit(errno_pipe_fd[1], -errno);
×
1419
                        }
1420

1421
                        r = send_one_fd(fd_pass_socket[1], dfd, /* flags = */ 0);
1✔
1422
                        dfd = safe_close(dfd);
1✔
1423
                        if (r < 0) {
1✔
UNCOV
1424
                                log_debug_errno(r, "Failed to send FD over socket: %m");
×
1425
                                report_errno_and_exit(errno_pipe_fd[1], r);
×
1426
                        }
1427

1428
                        _exit(EXIT_SUCCESS);
1✔
1429
                }
1430

1431
                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1✔
1432
                fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
1✔
1433

1434
                r = wait_for_terminate_and_check("(sd-openrootns)", child, /* flags = */ 0);
1✔
1435
                if (r < 0)
1✔
UNCOV
1436
                        return log_debug_errno(r, "Failed to wait for child: %m");
×
1437

1438
                r = read_errno(errno_pipe_fd[0]); /* the function does debug reporting */
1✔
1439
                if (r < 0)
1✔
1440
                        return r;
1441

1442
                int fd = receive_one_fd(fd_pass_socket[0], MSG_DONTWAIT);
1✔
1443
                if (fd < 0)
1✔
UNCOV
1444
                        return log_debug_errno(fd, "Failed to receive FD from child: %m");
×
1445

1446
                return fd;
1447
        }
1448

1449
        default:
1450
                return -EOPNOTSUPP;
1451
        }
1452
}
1453

1454
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
1455
        [MACHINE_CONTAINER] = "container",
1456
        [MACHINE_VM] = "vm",
1457
        [MACHINE_HOST] = "host",
1458
};
1459

1460
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
483✔
1461

1462
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
1463
        [MACHINE_OPENING] = "opening",
1464
        [MACHINE_RUNNING] = "running",
1465
        [MACHINE_CLOSING] = "closing"
1466
};
1467

1468
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
83✔
1469

1470
static const char* const kill_whom_table[_KILL_WHOM_MAX] = {
1471
        [KILL_LEADER] = "leader",
1472
        [KILL_ALL] = "all"
1473
};
1474

1475
DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom);
70✔
1476

1477
static const char* const acquire_metadata_table[_ACQUIRE_METADATA_MAX] = {
1478
        [ACQUIRE_METADATA_NO]       = "no",
1479
        [ACQUIRE_METADATA_YES]      = "yes",
1480
        [ACQUIRE_METADATA_GRACEFUL] = "graceful"
1481
};
1482

1483
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