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

systemd / systemd / 21652912420

03 Feb 2026 07:37PM UTC coverage: 72.767% (-0.04%) from 72.804%
21652912420

push

github

web-flow
Introduce name_to_handle_at_u64() helper function and use it for getting pidfd ID and cgroup ID (#40500)

26 of 28 new or added lines in 8 files covered. (92.86%)

2713 existing lines in 47 files now uncovered.

311340 of 427861 relevant lines covered (72.77%)

1144580.1 hits per line

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

82.76
/src/shared/bus-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <malloc.h>
5
#include <stdlib.h>
6
#include <sys/socket.h>
7
#include <sys/stat.h>
8
#include <unistd.h>
9

10
#include "sd-bus.h"
11
#include "sd-daemon.h"
12
#include "sd-event.h"
13
#include "sd-id128.h"
14

15
#include "alloc-util.h"
16
#include "bus-common-errors.h"
17
#include "bus-internal.h"
18
#include "bus-label.h"
19
#include "bus-util.h"
20
#include "capsule-util.h"
21
#include "chase.h"
22
#include "daemon-util.h"
23
#include "env-util.h"
24
#include "errno-util.h"
25
#include "fd-util.h"
26
#include "format-util.h"
27
#include "log.h"
28
#include "memfd-util.h"
29
#include "memstream-util.h"
30
#include "path-util.h"
31
#include "pidref.h"
32
#include "socket-util.h"
33
#include "stdio-util.h"
34
#include "string-table.h"
35
#include "string-util.h"
36
#include "strv.h"
37
#include "time-util.h"
38

39
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *reterr_error) {
18✔
40
        sd_event *e = ASSERT_PTR(userdata);
18✔
41

42
        assert(m);
18✔
43

44
        sd_bus_close(sd_bus_message_get_bus(m));
18✔
45
        sd_event_exit(e, 0);
18✔
46

47
        return 1;
18✔
48
}
49

UNCOV
50
int bus_log_address_error(int r, BusTransport transport) {
×
51
        bool hint = transport == BUS_TRANSPORT_LOCAL && r == -ENOMEDIUM;
×
52

UNCOV
53
        return log_error_errno(r,
×
54
                               hint ? "Failed to set bus address: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)" :
55
                                      "Failed to set bus address: %m");
56
}
57

58
int bus_log_connect_full(int log_level, int r, BusTransport transport, RuntimeScope scope) {
13✔
59
        bool hint_vars = transport == BUS_TRANSPORT_LOCAL && r == -ENOMEDIUM,
13✔
60
             hint_addr = transport == BUS_TRANSPORT_LOCAL && ERRNO_IS_PRIVILEGE(r);
13✔
61

62
        return log_full_errno(log_level, r,
25✔
63
                              hint_vars ? "Failed to connect to %s scope bus via %s transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)" :
64
                              hint_addr ? "Failed to connect to %s scope bus via %s transport: Operation not permitted (consider using --machine=<user>@.host --user to connect to bus of other user)" :
65
                                          "Failed to connect to %s scope bus via %s transport: %m", runtime_scope_to_string(scope), bus_transport_to_string(transport));
66
}
67

68
int bus_log_connect_error(int r, BusTransport transport, RuntimeScope scope) {
13✔
69
        return bus_log_connect_full(LOG_ERR, r, transport, scope);
13✔
70
}
71

72
int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
18✔
73
        const char *match;
18✔
74
        const char *unique;
18✔
75
        int r;
18✔
76

77
        assert(e);
18✔
78
        assert(bus);
18✔
79
        assert(name);
18✔
80

81
        /* We unregister the name here and then wait for the
82
         * NameOwnerChanged signal for this event to arrive before we
83
         * quit. We do this in order to make sure that any queued
84
         * requests are still processed before we really exit. */
85

86
        r = sd_bus_get_unique_name(bus, &unique);
18✔
87
        if (r < 0)
18✔
88
                return r;
18✔
89

90
        match = strjoina(
270✔
91
                        "sender='org.freedesktop.DBus',"
92
                        "type='signal',"
93
                        "interface='org.freedesktop.DBus',"
94
                        "member='NameOwnerChanged',"
95
                        "path='/org/freedesktop/DBus',"
96
                        "arg0='", name, "',",
97
                        "arg1='", unique, "',",
98
                        "arg2=''");
99

100
        r = sd_bus_add_match_async(bus, NULL, match, name_owner_change_callback, NULL, e);
18✔
101
        if (r < 0)
18✔
102
                return r;
103

104
        r = sd_bus_release_name_async(bus, NULL, name, NULL, NULL);
18✔
105
        if (r < 0)
18✔
UNCOV
106
                return r;
×
107

108
        return 0;
109
}
110

111
static bool idle_allowed(void) {
76,655✔
112
        static int allowed = -1;
76,655✔
113

114
        if (allowed >= 0)
76,655✔
UNCOV
115
                return allowed;
×
116

117
        allowed = secure_getenv_bool("SYSTEMD_EXIT_ON_IDLE");
76,655✔
118
        if (allowed < 0 && allowed != -ENXIO)
76,655✔
UNCOV
119
                log_debug_errno(allowed, "Failed to parse $SYSTEMD_EXIT_ON_IDLE, ignoring: %m");
×
120

121
        return allowed != 0;
76,655✔
122
}
123

124
int bus_event_loop_with_idle(
134✔
125
                sd_event *e,
126
                sd_bus *bus,
127
                const char *name,
128
                usec_t timeout,
129
                check_idle_t check_idle,
130
                void *userdata) {
131

132
        bool exiting = false;
134✔
133
        int r, code;
134✔
134

135
        assert(e);
134✔
136
        assert(bus);
134✔
137
        assert(name);
134✔
138

139
        for (;;) {
76,734✔
140
                bool idle;
76,734✔
141

142
                r = sd_event_get_state(e);
76,734✔
143
                if (r < 0)
76,734✔
144
                        return r;
79✔
145
                if (r == SD_EVENT_FINISHED)
76,734✔
146
                        break;
147

148
                if (!idle_allowed() || sd_bus_pending_method_calls(bus) > 0)
76,655✔
149
                        idle = false;
150
                else if (check_idle)
75,156✔
151
                        idle = check_idle(userdata);
75,156✔
152
                else
153
                        idle = true;
154

155
                r = sd_event_run(e, exiting || !idle ? UINT64_MAX : timeout);
143,462✔
156
                if (r < 0)
76,600✔
157
                        return r;
158

159
                if (r == 0 && !exiting && idle) {
76,600✔
160
                        log_debug("Idle for %s, exiting.", FORMAT_TIMESPAN(timeout, 1));
18✔
161

162
                        /* Inform the service manager that we are going down, so that it will queue all
163
                         * further start requests, instead of assuming we are still running. */
164
                        (void) sd_notify(false, NOTIFY_STOPPING_MESSAGE);
18✔
165

166
                        r = bus_async_unregister_and_exit(e, bus, name);
18✔
167
                        if (r < 0)
18✔
168
                                return r;
169

170
                        exiting = true;
171
                }
172
        }
173

174
        r = sd_event_get_exit_code(e, &code);
79✔
175
        if (r < 0)
79✔
176
                return r;
177

178
        return code;
79✔
179
}
180

181
int bus_name_has_owner(sd_bus *bus, const char *name, sd_bus_error *reterr_error) {
150✔
182
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL;
150✔
183
        int r, has_owner = 0;
150✔
184

185
        assert(bus);
150✔
186
        assert(name);
150✔
187

188
        r = sd_bus_call_method(bus,
150✔
189
                               "org.freedesktop.DBus",
190
                               "/org/freedesktop/dbus",
191
                               "org.freedesktop.DBus",
192
                               "NameHasOwner",
193
                               reterr_error,
194
                               &rep,
195
                               "s",
196
                               name);
197
        if (r < 0)
150✔
198
                return r;
199

200
        r = sd_bus_message_read_basic(rep, 'b', &has_owner);
150✔
201
        if (r < 0)
150✔
UNCOV
202
                return sd_bus_error_set_errno(reterr_error, r);
×
203

204
        return has_owner;
150✔
205
}
206

207
bool bus_error_is_unknown_service(const sd_bus_error *error) {
188✔
208
        return sd_bus_error_has_names(error,
188✔
209
                                      SD_BUS_ERROR_SERVICE_UNKNOWN,
210
                                      SD_BUS_ERROR_NAME_HAS_NO_OWNER,
211
                                      BUS_ERROR_NO_SUCH_UNIT);
212
}
213

214
bool bus_error_is_connection(const sd_bus_error *error) {
46✔
215
        return sd_bus_error_has_names(error,
46✔
216
                                      SD_BUS_ERROR_NO_REPLY,
217
                                      SD_BUS_ERROR_DISCONNECTED,
218
                                      SD_BUS_ERROR_TIMED_OUT);
219
}
220

221
int bus_check_peercred(sd_bus *bus) {
10,044✔
222
        struct ucred ucred;
10,044✔
223
        int fd, r;
10,044✔
224

225
        assert(bus);
10,044✔
226

227
        fd = sd_bus_get_fd(bus);
10,044✔
228
        if (fd < 0)
10,044✔
229
                return fd;
10,044✔
230

231
        r = getpeercred(fd, &ucred);
10,044✔
232
        if (r < 0)
10,044✔
233
                return r;
234

235
        if (ucred.uid != 0 && ucred.uid != geteuid())
10,044✔
UNCOV
236
                return -EPERM;
×
237

238
        return 1;
239
}
240

241
int bus_connect_system_systemd(sd_bus **ret) {
9,080✔
242
        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
9,080✔
243
        int r;
9,080✔
244

245
        assert(ret);
9,080✔
246

247
        r = sd_bus_new(&bus);
9,080✔
248
        if (r < 0)
9,080✔
249
                return r;
250

251
        r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
9,080✔
252
        if (r < 0)
9,080✔
253
                return r;
254

255
        r = sd_bus_start(bus);
9,080✔
256
        if (r < 0)
9,080✔
257
                return r;
258

259
        r = bus_check_peercred(bus);
9,080✔
260
        if (r < 0)
9,080✔
261
                return r;
262

263
        *ret = TAKE_PTR(bus);
9,080✔
264
        return 0;
9,080✔
265
}
266

267
int bus_connect_user_systemd(sd_bus **ret) {
239✔
268
        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
239✔
269
        _cleanup_free_ char *ee = NULL;
239✔
270
        const char *e;
239✔
271
        int r;
239✔
272

273
        assert(ret);
239✔
274

275
        e = secure_getenv("XDG_RUNTIME_DIR");
239✔
276
        if (!e)
239✔
277
                return -ENOMEDIUM;
278

279
        ee = bus_address_escape(e);
238✔
280
        if (!ee)
238✔
281
                return -ENOMEM;
282

283
        r = sd_bus_new(&bus);
238✔
284
        if (r < 0)
238✔
285
                return r;
286

287
        bus->address = strjoin("unix:path=", ee, "/systemd/private");
238✔
288
        if (!bus->address)
238✔
289
                return -ENOMEM;
290

291
        r = sd_bus_start(bus);
238✔
292
        if (r < 0)
238✔
293
                return r;
294

295
        r = bus_check_peercred(bus);
238✔
296
        if (r < 0)
238✔
297
                return r;
298

299
        *ret = TAKE_PTR(bus);
238✔
300
        return 0;
238✔
301
}
302

303
static int pin_capsule_socket(const char *capsule, const char *suffix, uid_t *ret_uid, gid_t *ret_gid) {
5✔
304
        _cleanup_close_ int inode_fd = -EBADF;
5✔
305
        _cleanup_free_ char *p = NULL;
5✔
306
        struct stat st;
5✔
307
        int r;
5✔
308

309
        assert(capsule);
5✔
310
        assert(suffix);
5✔
311
        assert(ret_uid);
5✔
312
        assert(ret_gid);
5✔
313

314
        p = path_join("/run/capsules", capsule, suffix);
5✔
315
        if (!p)
5✔
316
                return -ENOMEM;
317

318
        /* We enter territory owned by the user, hence let's be paranoid about symlinks */
319
        r = chase(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &inode_fd);
5✔
320
        if (r < 0)
5✔
321
                return r;
322

323
        if (fstat(inode_fd, &st) < 0)
5✔
UNCOV
324
                return negative_errno();
×
325

326
        *ret_uid = st.st_uid;
5✔
327
        *ret_gid = st.st_gid;
5✔
328

329
        return TAKE_FD(inode_fd);
5✔
330
}
331

332
static int bus_set_address_capsule(sd_bus *bus, const char *capsule, const char *suffix, int *ret_pin_fd) {
5✔
333
        _cleanup_close_ int inode_fd = -EBADF;
5✔
334
        _cleanup_free_ char *pp = NULL;
5✔
335
        uid_t uid;
5✔
336
        gid_t gid;
5✔
337
        int r;
5✔
338

339
        assert(bus);
5✔
340
        assert(capsule);
5✔
341
        assert(suffix);
5✔
342
        assert(ret_pin_fd);
5✔
343

344
        /* Connects to a capsule's user bus. We need to do so under the capsule's UID/GID, otherwise
345
         * the service manager might refuse our connection. Hence fake it. */
346

347
        r = capsule_name_is_valid(capsule);
5✔
348
        if (r < 0)
5✔
349
                return r;
350
        if (r == 0)
5✔
351
                return -EINVAL;
352

353
        inode_fd = pin_capsule_socket(capsule, suffix, &uid, &gid);
5✔
354
        if (inode_fd < 0)
5✔
355
                return inode_fd;
356

357
        pp = bus_address_escape(FORMAT_PROC_FD_PATH(inode_fd));
5✔
358
        if (!pp)
5✔
359
                return -ENOMEM;
360

361
        if (asprintf(&bus->address, "unix:path=%s,uid=" UID_FMT ",gid=" GID_FMT, pp, uid, gid) < 0)
5✔
362
                return -ENOMEM;
363

364
        *ret_pin_fd = TAKE_FD(inode_fd); /* This fd must be kept pinned until the connection has been established */
5✔
365
        return 0;
5✔
366
}
367

368
int bus_set_address_capsule_bus(sd_bus *bus, const char *capsule, int *ret_pin_fd) {
1✔
369
        return bus_set_address_capsule(bus, capsule, "bus", ret_pin_fd);
1✔
370
}
371

372
int bus_connect_capsule_systemd(const char *capsule, sd_bus **ret) {
4✔
373
        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
4✔
374
        _cleanup_close_ int inode_fd = -EBADF;
4✔
375
        int r;
4✔
376

377
        assert(capsule);
4✔
378
        assert(ret);
4✔
379

380
        r = sd_bus_new(&bus);
4✔
381
        if (r < 0)
4✔
382
                return r;
383

384
        r = bus_set_address_capsule(bus, capsule, "systemd/private", &inode_fd);
4✔
385
        if (r < 0)
4✔
386
                return r;
387

388
        r = sd_bus_start(bus);
4✔
389
        if (r < 0)
4✔
390
                return r;
391

392
        *ret = TAKE_PTR(bus);
4✔
393
        return 0;
4✔
394
}
395

UNCOV
396
int bus_connect_capsule_bus(const char *capsule, sd_bus **ret) {
×
UNCOV
397
        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
×
UNCOV
398
        _cleanup_close_ int inode_fd = -EBADF;
×
UNCOV
399
        int r;
×
400

401
        assert(capsule);
×
402
        assert(ret);
×
403

404
        r = sd_bus_new(&bus);
×
UNCOV
405
        if (r < 0)
×
406
                return r;
407

UNCOV
408
        r = bus_set_address_capsule_bus(bus, capsule, &inode_fd);
×
409
        if (r < 0)
×
410
                return r;
411

UNCOV
412
        r = sd_bus_set_bus_client(bus, true);
×
413
        if (r < 0)
×
414
                return r;
415

UNCOV
416
        r = sd_bus_start(bus);
×
417
        if (r < 0)
×
418
                return r;
419

UNCOV
420
        *ret = TAKE_PTR(bus);
×
421
        return 0;
×
422
}
423

424
int bus_connect_transport(
2,710✔
425
                BusTransport transport,
426
                const char *host,
427
                RuntimeScope runtime_scope,
428
                sd_bus **ret) {
429

430
        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
2,710✔
431
        int r;
2,710✔
432

433
        assert(transport >= 0);
2,710✔
434
        assert(transport < _BUS_TRANSPORT_MAX);
2,710✔
435
        assert(ret);
2,710✔
436

437
        switch (transport) {
2,710✔
438

439
        case BUS_TRANSPORT_LOCAL:
2,593✔
440
                assert_return(!host, -EINVAL);
2,593✔
441

442
                switch (runtime_scope) {
2,593✔
443

444
                case RUNTIME_SCOPE_USER:
135✔
445
                        r = sd_bus_default_user(&bus);
135✔
446
                        break;
447

448
                case RUNTIME_SCOPE_SYSTEM:
2,458✔
449
                        if (sd_booted() <= 0)
2,458✔
450
                                /* Print a friendly message when the local system is actually not running systemd as PID 1. */
UNCOV
451
                                return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
×
452
                                                       "System has not been booted with systemd as init system (PID 1). Can't operate.");
453

454
                        r = sd_bus_default_system(&bus);
2,458✔
455
                        break;
456

UNCOV
457
                default:
×
UNCOV
458
                        assert_not_reached();
×
459
                }
460
                break;
461

462
        case BUS_TRANSPORT_REMOTE:
×
463
                assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
×
464

UNCOV
465
                r = sd_bus_open_system_remote(&bus, host);
×
466
                break;
467

468
        case BUS_TRANSPORT_MACHINE:
117✔
469
                switch (runtime_scope) {
117✔
470

471
                case RUNTIME_SCOPE_USER:
73✔
472
                        r = sd_bus_open_user_machine(&bus, host);
73✔
473
                        break;
474

475
                case RUNTIME_SCOPE_SYSTEM:
44✔
476
                        r = sd_bus_open_system_machine(&bus, host);
44✔
477
                        break;
478

UNCOV
479
                default:
×
UNCOV
480
                        assert_not_reached();
×
481
                }
482

483
                break;
484

485
        case BUS_TRANSPORT_CAPSULE:
×
UNCOV
486
                assert_return(runtime_scope == RUNTIME_SCOPE_USER, -EINVAL);
×
487

UNCOV
488
                r = bus_connect_capsule_bus(host, &bus);
×
489
                break;
490

491
        default:
×
UNCOV
492
                assert_not_reached();
×
493
        }
494
        if (r < 0)
2,710✔
495
                return r;
496

497
        r = sd_bus_set_exit_on_disconnect(bus, true);
2,698✔
498
        if (r < 0)
2,698✔
499
                return r;
500

501
        *ret = TAKE_PTR(bus);
2,698✔
502
        return 0;
2,698✔
503
}
504

505
int bus_connect_transport_systemd(
9,205✔
506
                BusTransport transport,
507
                const char *host,
508
                RuntimeScope runtime_scope,
509
                sd_bus **ret) {
510

511
        int r;
9,205✔
512

513
        assert(transport >= 0);
9,205✔
514
        assert(transport < _BUS_TRANSPORT_MAX);
9,205✔
515
        assert(ret);
9,205✔
516

517
        switch (transport) {
9,205✔
518

519
        case BUS_TRANSPORT_LOCAL:
9,200✔
520
                assert_return(!host, -EINVAL);
9,200✔
521

522
                switch (runtime_scope) {
9,200✔
523

524
                case RUNTIME_SCOPE_USER:
239✔
525
                        r = bus_connect_user_systemd(ret);
239✔
526
                        /* We used to always fall back to the user session bus if we couldn't connect to the
527
                         * private manager bus. To keep compat with existing code that was setting
528
                         * DBUS_SESSION_BUS_ADDRESS without setting XDG_RUNTIME_DIR, connect to the user
529
                         * session bus if DBUS_SESSION_BUS_ADDRESS is set and XDG_RUNTIME_DIR isn't. */
530
                        if (r == -ENOMEDIUM && secure_getenv("DBUS_SESSION_BUS_ADDRESS")) {
239✔
UNCOV
531
                                log_debug_errno(r, "$XDG_RUNTIME_DIR not set, unable to connect to private bus. Falling back to session bus.");
×
UNCOV
532
                                r = sd_bus_default_user(ret);
×
533
                        }
534

535
                        return r;
536

537
                case RUNTIME_SCOPE_SYSTEM:
8,961✔
538
                        if (sd_booted() <= 0)
8,961✔
539
                                /* Print a friendly message when the local system is actually not running systemd as PID 1. */
UNCOV
540
                                return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
×
541
                                                       "System has not been booted with systemd as init system (PID 1). Can't operate.");
542

543
                        /* If we are root then let's talk directly to the system instance, instead of
544
                         * going via the bus. */
545
                        if (geteuid() == 0)
8,961✔
546
                                return bus_connect_system_systemd(ret);
8,947✔
547

548
                        return sd_bus_default_system(ret);
14✔
549

UNCOV
550
                default:
×
UNCOV
551
                        assert_not_reached();
×
552
                }
553

UNCOV
554
                break;
×
555

556
        case BUS_TRANSPORT_REMOTE:
×
UNCOV
557
                assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
×
UNCOV
558
                return sd_bus_open_system_remote(ret, host);
×
559

560
        case BUS_TRANSPORT_MACHINE:
1✔
561
                assert_return(runtime_scope == RUNTIME_SCOPE_SYSTEM, -EOPNOTSUPP);
1✔
562
                return sd_bus_open_system_machine(ret, host);
1✔
563

564
        case BUS_TRANSPORT_CAPSULE:
4✔
565
                assert_return(runtime_scope == RUNTIME_SCOPE_USER, -EINVAL);
4✔
566
                return bus_connect_capsule_systemd(host, ret);
4✔
567

UNCOV
568
        default:
×
UNCOV
569
                assert_not_reached();
×
570
        }
571
}
572

573
/**
574
 * bus_path_encode_unique() - encode unique object path
575
 * @b: bus connection or NULL
576
 * @prefix: object path prefix
577
 * @sender_id: unique-name of client, or NULL
578
 * @external_id: external ID to be chosen by client, or NULL
579
 * @ret_path: storage for encoded object path pointer
580
 *
581
 * Whenever we provide a bus API that allows clients to create and manage
582
 * server-side objects, we need to provide a unique name for these objects. If
583
 * we let the server choose the name, we suffer from a race condition: If a
584
 * client creates an object asynchronously, it cannot destroy that object until
585
 * it received the method reply. It cannot know the name of the new object,
586
 * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
587
 *
588
 * Therefore, many APIs allow the client to choose the unique name for newly
589
 * created objects. There're two problems to solve, though:
590
 *    1) Object names are usually defined via dbus object paths, which are
591
 *       usually globally namespaced. Therefore, multiple clients must be able
592
 *       to choose unique object names without interference.
593
 *    2) If multiple libraries share the same bus connection, they must be
594
 *       able to choose unique object names without interference.
595
 * The first problem is solved easily by prefixing a name with the
596
 * unique-bus-name of a connection. The server side must enforce this and
597
 * reject any other name. The second problem is solved by providing unique
598
 * suffixes from within sd-bus.
599
 *
600
 * This helper allows clients to create unique object-paths. It uses the
601
 * template '/prefix/sender_id/external_id' and returns the new path in
602
 * @ret_path (must be freed by the caller).
603
 * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
604
 * NULL, this function allocates a unique suffix via @b (by requesting a new
605
 * cookie). If both @sender_id and @external_id are given, @b can be passed as
606
 * NULL.
607
 *
608
 * Returns: 0 on success, negative error code on failure.
609
 */
610
int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
1✔
611
        _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
1✔
612
        char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
1✔
613
        int r;
1✔
614

615
        assert_return(b || (sender_id && external_id), -EINVAL);
1✔
616
        assert_return(sd_bus_object_path_is_valid(prefix), -EINVAL);
1✔
617
        assert_return(ret_path, -EINVAL);
1✔
618

619
        if (!sender_id) {
1✔
UNCOV
620
                r = sd_bus_get_unique_name(b, &sender_id);
×
UNCOV
621
                if (r < 0)
×
622
                        return r;
623
        }
624

625
        if (!external_id) {
1✔
626
                xsprintf(external_buf, "%"PRIu64, ++b->cookie);
×
627
                external_id = external_buf;
628
        }
629

630
        sender_label = bus_label_escape(sender_id);
1✔
631
        if (!sender_label)
1✔
632
                return -ENOMEM;
633

634
        external_label = bus_label_escape(external_id);
1✔
635
        if (!external_label)
1✔
636
                return -ENOMEM;
637

638
        p = path_join(prefix, sender_label, external_label);
1✔
639
        if (!p)
1✔
640
                return -ENOMEM;
641

642
        *ret_path = p;
1✔
643
        return 0;
1✔
644
}
645

646
/**
647
 * bus_path_decode_unique() - decode unique object path
648
 * @path: object path to decode
649
 * @prefix: object path prefix
650
 * @ret_sender: output parameter for sender-id label
651
 * @ret_external: output parameter for external-id label
652
 *
653
 * This does the reverse of bus_path_encode_unique() (see its description for
654
 * details). Both trailing labels, sender-id and external-id, are unescaped and
655
 * returned in the given output parameters (the caller must free them).
656
 *
657
 * Note that this function returns 0 if the path does not match the template
658
 * (see bus_path_encode_unique()), 1 if it matched.
659
 *
660
 * Returns: Negative error code on failure, 0 if the given object path does not
661
 *          match the template (return parameters are set to NULL), 1 if it was
662
 *          parsed successfully (return parameters contain allocated labels).
663
 */
664
int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
4✔
665
        const char *p, *q;
4✔
666
        char *sender, *external;
4✔
667

668
        assert(sd_bus_object_path_is_valid(path));
4✔
669
        assert(sd_bus_object_path_is_valid(prefix));
4✔
670
        assert(ret_sender);
4✔
671
        assert(ret_external);
4✔
672

673
        p = object_path_startswith(path, prefix);
4✔
674
        if (!p) {
4✔
675
                *ret_sender = NULL;
1✔
676
                *ret_external = NULL;
1✔
677
                return 0;
1✔
678
        }
679

680
        q = strchr(p, '/');
3✔
681
        if (!q) {
3✔
682
                *ret_sender = NULL;
1✔
683
                *ret_external = NULL;
1✔
684
                return 0;
1✔
685
        }
686

687
        sender = bus_label_unescape_n(p, q - p);
2✔
688
        external = bus_label_unescape(q + 1);
2✔
689
        if (!sender || !external) {
2✔
UNCOV
690
                free(sender);
×
UNCOV
691
                free(external);
×
UNCOV
692
                return -ENOMEM;
×
693
        }
694

695
        *ret_sender = sender;
2✔
696
        *ret_external = external;
2✔
697
        return 1;
2✔
698
}
699

700
int bus_track_add_name_many(sd_bus_track *t, char * const *l) {
17✔
701
        int r = 0;
17✔
702

703
        assert(t);
17✔
704

705
        /* Continues adding after failure, and returns the first failure. */
706

707
        STRV_FOREACH(i, l)
50✔
708
                RET_GATHER(r, sd_bus_track_add_name(t, *i));
33✔
709
        return r;
17✔
710
}
711

712
int bus_track_to_strv(sd_bus_track *t, char ***ret) {
74✔
713
        _cleanup_strv_free_ char **subscribed = NULL;
74✔
714
        int r;
74✔
715

716
        assert(ret);
74✔
717

718
        for (const char *n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
176✔
719
                int c = sd_bus_track_count_name(t, n);
102✔
720
                assert(c >= 0);
102✔
721

722
                for (int j = 0; j < c; j++) {
204✔
723
                        r = strv_extend(&subscribed, n);
102✔
724
                        if (r < 0)
102✔
725
                                return r;
726
                }
727
        }
728

729
        *ret = TAKE_PTR(subscribed);
74✔
730
        return 0;
74✔
731
}
732

733
int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description) {
646✔
734
        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
646✔
735
        const char *e;
646✔
736
        int r;
646✔
737

738
        assert(ret);
646✔
739

740
        /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal
741
         * turned on. */
742

743
        r = sd_bus_new(&bus);
646✔
744
        if (r < 0)
646✔
745
                return r;
746

747
        if (description) {
646✔
748
                r = sd_bus_set_description(bus, description);
641✔
749
                if (r < 0)
641✔
750
                        return r;
751
        }
752

753
        e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
646✔
754
        if (!e)
646✔
755
                e = DEFAULT_SYSTEM_BUS_ADDRESS;
646✔
756

757
        r = sd_bus_set_address(bus, e);
646✔
758
        if (r < 0)
646✔
759
                return r;
760

761
        r = sd_bus_set_bus_client(bus, true);
646✔
762
        if (r < 0)
646✔
763
                return r;
764

765
        r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
646✔
766
        if (r < 0)
646✔
767
                return r;
768

769
        r = sd_bus_set_watch_bind(bus, true);
646✔
770
        if (r < 0)
646✔
771
                return r;
772

773
        r = sd_bus_set_connected_signal(bus, true);
646✔
774
        if (r < 0)
646✔
775
                return r;
776

777
        r = sd_bus_start(bus);
646✔
778
        if (r < 0)
646✔
779
                return r;
780

781
        *ret = TAKE_PTR(bus);
646✔
782

783
        return 0;
646✔
784
}
785

786
int bus_reply_pair_array(sd_bus_message *m, char * const *l) {
71✔
787
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
71✔
788
        int r;
71✔
789

790
        assert(m);
71✔
791

792
        /* Reply to the specified message with a message containing a dictionary put together from the
793
         * specified strv */
794

795
        r = sd_bus_message_new_method_return(m, &reply);
71✔
796
        if (r < 0)
71✔
797
                return r;
798

799
        r = sd_bus_message_open_container(reply, 'a', "{ss}");
71✔
800
        if (r < 0)
71✔
801
                return r;
802

803
        STRV_FOREACH_PAIR(k, v, l) {
577✔
804
                r = sd_bus_message_append(reply, "{ss}", *k, *v);
506✔
805
                if (r < 0)
506✔
806
                        return r;
807
        }
808

809
        r = sd_bus_message_close_container(reply);
71✔
810
        if (r < 0)
71✔
811
                return r;
812

813
        return sd_bus_message_send(reply);
71✔
814
}
815

UNCOV
816
static int method_dump_memory_state_by_fd(sd_bus_message *message, void *userdata, sd_bus_error *reterr_error) {
×
UNCOV
817
        _cleanup_(memstream_done) MemStream m = {};
×
UNCOV
818
        _cleanup_free_ char *dump = NULL;
×
UNCOV
819
        _cleanup_close_ int fd = -EBADF;
×
UNCOV
820
        size_t dump_size;
×
821
        FILE *f;
×
822
        int r;
×
823

824
        assert(message);
×
825

826
        f = memstream_init(&m);
×
827
        if (!f)
×
828
                return -ENOMEM;
829

UNCOV
830
        r = RET_NERRNO(malloc_info(/* options= */ 0, f));
×
831
        if (r < 0)
×
832
                return r;
833

UNCOV
834
        r = memstream_finalize(&m, &dump, &dump_size);
×
835
        if (r < 0)
×
836
                return r;
837

UNCOV
838
        fd = memfd_new_and_seal("malloc-info", dump, dump_size);
×
839
        if (fd < 0)
×
840
                return fd;
841

UNCOV
842
        r = sd_bus_reply_method_return(message, "h", fd);
×
843
        if (r < 0)
×
844
                return r;
×
845

846
        return 1; /* Stop further processing */
847
}
848

849
/* The default install callback will fail and disconnect the bus if it cannot register the match, but this
850
 * is only a debug method, we definitely don't want to fail in case there's some permission issue. */
851
static int dummy_install_callback(sd_bus_message *message, void *userdata, sd_bus_error *reterr_error) {
73✔
852
        return 1;
73✔
853
}
854

855
int bus_register_malloc_status(sd_bus *bus, const char *destination) {
800✔
856
        const char *match;
800✔
857
        int r;
800✔
858

859
        assert(bus);
800✔
860
        assert(!isempty(destination));
800✔
861

862
        match = strjoina("type='method_call',"
7,200✔
863
                         "interface='org.freedesktop.MemoryAllocation1',"
864
                         "path='/org/freedesktop/MemoryAllocation1',"
865
                         "destination='", destination, "',",
866
                         "member='GetMallocInfo'");
867

868
        r = sd_bus_add_match_async(bus, NULL, match, method_dump_memory_state_by_fd, dummy_install_callback, NULL);
800✔
869
        if (r < 0)
800✔
UNCOV
870
                return log_debug_errno(r, "Failed to subscribe to GetMallocInfo() calls on MemoryAllocation1 interface: %m");
×
871

872
        return 0;
873
}
874

875
int bus_creds_get_pidref(
158✔
876
                sd_bus_creds *c,
877
                PidRef *ret) {
878

879
        int pidfd = -EBADF;
158✔
880
        pid_t pid;
158✔
881
        int r;
158✔
882

883
        assert(c);
158✔
884
        assert(ret);
158✔
885

886
        r = sd_bus_creds_get_pid(c, &pid);
158✔
887
        if (r < 0)
158✔
888
                return r;
158✔
889

890
        r = sd_bus_creds_get_pidfd_dup(c, &pidfd);
158✔
891
        if (r < 0 && r != -ENODATA)
158✔
892
                return r;
893

894
        *ret = (PidRef) {
158✔
895
                .pid = pid,
896
                .fd = pidfd,
897
        };
898

899
        return 0;
158✔
900
}
901

902
int bus_query_sender_pidref(
68✔
903
                sd_bus_message *m,
904
                PidRef *ret) {
905

906
        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
68✔
907
        int r;
68✔
908

909
        assert(m);
68✔
910
        assert(ret);
68✔
911

912
        r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_PID|SD_BUS_CREDS_PIDFD, &creds);
68✔
913
        if (r < 0)
68✔
914
                return r;
915

916
        return bus_creds_get_pidref(creds, ret);
68✔
917
}
918

919
int bus_get_instance_id(sd_bus *bus, sd_id128_t *ret) {
74✔
920
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
74✔
921
        int r;
74✔
922

923
        assert(bus);
74✔
924
        assert(ret);
74✔
925

926
        r = sd_bus_call_method(bus,
74✔
927
                               "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetId",
928
                               /* reterr_error= */ NULL, &reply, NULL);
929
        if (r < 0)
74✔
930
                return r;
931

932
        const char *id;
74✔
933

934
        r = sd_bus_message_read_basic(reply, 's', &id);
74✔
935
        if (r < 0)
74✔
936
                return r;
937

938
        return sd_id128_from_string(id, ret);
74✔
939
}
940

941
static const char* const bus_transport_table[] = {
942
        [BUS_TRANSPORT_LOCAL]   = "local",
943
        [BUS_TRANSPORT_REMOTE]  = "remote",
944
        [BUS_TRANSPORT_MACHINE] = "machine",
945
        [BUS_TRANSPORT_CAPSULE] = "capsule",
946
};
947

948
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(bus_transport, BusTransport);
13✔
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