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

systemd / systemd / 13168948113

05 Feb 2025 10:37PM UTC coverage: 71.813% (-0.004%) from 71.817%
13168948113

push

github

poettering
update TODO

293032 of 408051 relevant lines covered (71.81%)

710489.93 hits per line

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

86.33
/src/libsystemd/sd-bus/bus-objects.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "alloc-util.h"
4
#include "bus-internal.h"
5
#include "bus-introspect.h"
6
#include "bus-message.h"
7
#include "bus-objects.h"
8
#include "bus-signature.h"
9
#include "bus-slot.h"
10
#include "bus-type.h"
11
#include "missing_capability.h"
12
#include "string-util.h"
13
#include "strv.h"
14

15
static int node_vtable_get_userdata(
383,201✔
16
                sd_bus *bus,
17
                const char *path,
18
                struct node_vtable *c,
19
                void **userdata,
20
                sd_bus_error *error) {
21

22
        sd_bus_slot *s;
383,201✔
23
        void *u, *found_u = NULL;
383,201✔
24
        int r;
383,201✔
25

26
        assert(bus);
383,201✔
27
        assert(path);
383,201✔
28
        assert(c);
383,201✔
29

30
        s = container_of(c, sd_bus_slot, node_vtable);
383,201✔
31
        u = s->userdata;
383,201✔
32
        if (c->find) {
383,201✔
33
                bus->current_slot = sd_bus_slot_ref(s);
344,516✔
34
                bus->current_userdata = u;
344,516✔
35
                r = c->find(bus, path, c->interface, u, &found_u, error);
344,516✔
36
                bus->current_userdata = NULL;
344,516✔
37
                bus->current_slot = sd_bus_slot_unref(s);
344,516✔
38

39
                if (r < 0)
344,516✔
40
                        return r;
383,201✔
41
                if (sd_bus_error_is_set(error))
344,516✔
42
                        return -sd_bus_error_get_errno(error);
1✔
43
                if (r == 0)
344,515✔
44
                        return r;
45
        } else
46
                found_u = u;
38,685✔
47

48
        if (userdata)
342,459✔
49
                *userdata = found_u;
340,534✔
50

51
        return 1;
52
}
53

54
static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
6,976✔
55
        assert(p);
6,976✔
56

57
        if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
6,976✔
58
                return SIZE_TO_PTR(p->x.method.offset); /* don't add offset on NULL, to make ubsan happy */
3✔
59

60
        return (uint8_t*) u + p->x.method.offset;
6,973✔
61
}
62

63
static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
1,674,970✔
64
        assert(p);
1,674,970✔
65

66
        if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
1,674,970✔
67
                return SIZE_TO_PTR(p->x.property.offset); /* as above */
26✔
68

69
        return (uint8_t*) u + p->x.property.offset;
1,674,944✔
70
}
71

72
static int vtable_property_get_userdata(
13,465✔
73
                sd_bus *bus,
74
                const char *path,
75
                struct vtable_member *p,
76
                void **userdata,
77
                sd_bus_error *error) {
78

79
        void *u;
13,465✔
80
        int r;
13,465✔
81

82
        assert(bus);
13,465✔
83
        assert(path);
13,465✔
84
        assert(p);
13,465✔
85
        assert(userdata);
13,465✔
86

87
        r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
13,465✔
88
        if (r <= 0)
13,465✔
89
                return r;
13,465✔
90
        if (bus->nodes_modified)
12,354✔
91
                return 0;
92

93
        *userdata = vtable_property_convert_userdata(p->vtable, u);
12,354✔
94
        return 1;
12,354✔
95
}
96

97
static int add_enumerated_to_set(
2,077✔
98
                sd_bus *bus,
99
                const char *prefix,
100
                struct node_enumerator *first,
101
                OrderedSet *s,
102
                sd_bus_error *error) {
103

104
        int r;
2,077✔
105

106
        assert(bus);
2,077✔
107
        assert(prefix);
2,077✔
108
        assert(s);
2,077✔
109

110
        LIST_FOREACH(enumerators, c, first) {
2,585✔
111
                char **children = NULL;
508✔
112
                sd_bus_slot *slot;
508✔
113

114
                if (bus->nodes_modified)
508✔
115
                        return 0;
×
116

117
                slot = container_of(c, sd_bus_slot, node_enumerator);
508✔
118

119
                bus->current_slot = sd_bus_slot_ref(slot);
508✔
120
                bus->current_userdata = slot->userdata;
508✔
121
                r = c->callback(bus, prefix, slot->userdata, &children, error);
508✔
122
                bus->current_userdata = NULL;
508✔
123
                bus->current_slot = sd_bus_slot_unref(slot);
508✔
124

125
                if (r < 0)
508✔
126
                        return r;
127
                if (sd_bus_error_is_set(error))
508✔
128
                        return -sd_bus_error_get_errno(error);
×
129

130
                STRV_FOREACH(k, children) {
1,005✔
131
                        if (r < 0) {
497✔
132
                                free(*k);
×
133
                                continue;
×
134
                        }
135

136
                        if (!object_path_is_valid(*k)) {
497✔
137
                                free(*k);
×
138
                                r = -EINVAL;
×
139
                                continue;
×
140
                        }
141

142
                        if (!object_path_startswith(*k, prefix)) {
497✔
143
                                free(*k);
72✔
144
                                continue;
72✔
145
                        }
146

147
                        r = ordered_set_consume(s, *k);
425✔
148
                        if (r == -EEXIST)
425✔
149
                                r = 0;
6✔
150
                }
151

152
                free(children);
508✔
153
                if (r < 0)
508✔
154
                        return r;
155
        }
156

157
        return 0;
158
}
159

160
enum {
161
        /* if set, add_subtree() works recursively */
162
        CHILDREN_RECURSIVE      = 1 << 0,
163
        /* if set, add_subtree() scans object-manager hierarchies recursively */
164
        CHILDREN_SUBHIERARCHIES = 1 << 1,
165
};
166

167
static int add_subtree_to_set(
2,077✔
168
                sd_bus *bus,
169
                const char *prefix,
170
                struct node *n,
171
                unsigned flags,
172
                OrderedSet *s,
173
                sd_bus_error *error) {
174

175
        int r;
2,077✔
176

177
        assert(bus);
2,077✔
178
        assert(prefix);
2,077✔
179
        assert(n);
2,077✔
180
        assert(s);
2,077✔
181

182
        r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
2,077✔
183
        if (r < 0)
2,077✔
184
                return r;
185
        if (bus->nodes_modified)
2,077✔
186
                return 0;
187

188
        LIST_FOREACH(siblings, i, n->child) {
3,659✔
189
                char *t;
1,584✔
190

191
                if (!object_path_startswith(i->path, prefix))
1,584✔
192
                        continue;
×
193

194
                t = strdup(i->path);
1,584✔
195
                if (!t)
1,584✔
196
                        return -ENOMEM;
197

198
                r = ordered_set_consume(s, t);
1,584✔
199
                if (r < 0 && r != -EEXIST)
1,584✔
200
                        return r;
201

202
                if ((flags & CHILDREN_RECURSIVE) &&
1,584✔
203
                    ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
2✔
204
                        r = add_subtree_to_set(bus, prefix, i, flags, s, error);
1✔
205
                        if (r < 0)
1✔
206
                                return r;
207
                        if (bus->nodes_modified)
1✔
208
                                return 0;
209
                }
210
        }
211

212
        return 0;
213
}
214

215
static int get_child_nodes(
2,076✔
216
                sd_bus *bus,
217
                const char *prefix,
218
                struct node *n,
219
                unsigned flags,
220
                OrderedSet **ret,
221
                sd_bus_error *error) {
222

223
        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
2,076✔
224
        int r;
2,076✔
225

226
        assert(bus);
2,076✔
227
        assert(prefix);
2,076✔
228
        assert(n);
2,076✔
229
        assert(ret);
2,076✔
230

231
        s = ordered_set_new(&string_hash_ops_free);
2,076✔
232
        if (!s)
2,076✔
233
                return -ENOMEM;
234

235
        r = add_subtree_to_set(bus, prefix, n, flags, s, error);
2,076✔
236
        if (r < 0)
2,076✔
237
                return r;
238

239
        *ret = TAKE_PTR(s);
2,076✔
240
        return 0;
2,076✔
241
}
242

243
static int node_callbacks_run(
29,124✔
244
                sd_bus *bus,
245
                sd_bus_message *m,
246
                struct node_callback *first,
247
                bool require_fallback,
248
                bool *found_object) {
249

250
        int r;
29,124✔
251

252
        assert(bus);
29,124✔
253
        assert(m);
29,124✔
254
        assert(found_object);
29,124✔
255

256
        LIST_FOREACH(callbacks, c, first) {
29,124✔
257
                _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
×
258
                sd_bus_slot *slot;
×
259

260
                if (bus->nodes_modified)
×
261
                        return 0;
262

263
                if (require_fallback && !c->is_fallback)
×
264
                        continue;
×
265

266
                *found_object = true;
×
267

268
                if (c->last_iteration == bus->iteration_counter)
×
269
                        continue;
×
270

271
                c->last_iteration = bus->iteration_counter;
×
272

273
                r = sd_bus_message_rewind(m, true);
×
274
                if (r < 0)
×
275
                        return r;
276

277
                slot = container_of(c, sd_bus_slot, node_callback);
×
278

279
                bus->current_slot = sd_bus_slot_ref(slot);
×
280
                bus->current_handler = c->callback;
×
281
                bus->current_userdata = slot->userdata;
×
282
                r = c->callback(m, slot->userdata, &error_buffer);
×
283
                bus->current_userdata = NULL;
×
284
                bus->current_handler = NULL;
×
285
                bus->current_slot = sd_bus_slot_unref(slot);
×
286

287
                r = bus_maybe_reply_error(m, r, &error_buffer);
×
288
                if (r != 0)
×
289
                        return r;
290
        }
291

292
        return 0;
293
}
294

295
#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
296

297
static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
7,007✔
298
        uint64_t cap;
7,007✔
299
        int r;
7,007✔
300

301
        assert(bus);
7,007✔
302
        assert(m);
7,007✔
303
        assert(c);
7,007✔
304

305
        /* If the entire bus is trusted let's grant access */
306
        if (bus->trusted)
7,007✔
307
                return 0;
308

309
        /* If the member is marked UNPRIVILEGED let's grant access */
310
        if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
6,901✔
311
                return 0;
312

313
        /* Check that the caller has the requested capability set. Note that the flags value contains the
314
         * capability number plus one, which we need to subtract here. We do this so that we have 0 as
315
         * special value for the default. */
316
        cap = CAPABILITY_SHIFT(c->vtable->flags);
315✔
317
        if (cap == 0)
315✔
318
                cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
254✔
319
        if (cap == 0)
254✔
320
                cap = CAP_SYS_ADMIN;
321
        else
322
                cap--;
61✔
323

324
        r = sd_bus_query_sender_privilege(m, cap);
315✔
325
        if (r < 0)
315✔
326
                return r;
327
        if (r > 0)
315✔
328
                return 0;
329

330
        return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
×
331
}
332

333
static int method_callbacks_run(
6,976✔
334
                sd_bus *bus,
335
                sd_bus_message *m,
336
                struct vtable_member *c,
337
                bool require_fallback,
338
                bool *found_object) {
339

340
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
6,921✔
341
        const char *signature;
6,976✔
342
        void *u;
6,976✔
343
        int r;
6,976✔
344

345
        assert(bus);
6,976✔
346
        assert(m);
6,976✔
347
        assert(c);
6,976✔
348
        assert(found_object);
6,976✔
349

350
        if (require_fallback && !c->parent->is_fallback)
6,976✔
351
                return 0;
352

353
        if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
6,976✔
354
                r = sd_bus_message_sensitive(m);
291✔
355
                if (r < 0)
291✔
356
                        return r;
357
        }
358

359
        r = check_access(bus, m, c, &error);
6,976✔
360
        if (r < 0)
6,976✔
361
                return bus_maybe_reply_error(m, r, &error);
×
362

363
        r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
6,976✔
364
        if (r <= 0)
6,976✔
365
                return bus_maybe_reply_error(m, r, &error);
×
366
        if (bus->nodes_modified)
6,976✔
367
                return 0;
368

369
        u = vtable_method_convert_userdata(c->vtable, u);
6,976✔
370

371
        *found_object = true;
6,976✔
372

373
        if (c->last_iteration == bus->iteration_counter)
6,976✔
374
                return 0;
375

376
        c->last_iteration = bus->iteration_counter;
6,976✔
377

378
        r = sd_bus_message_rewind(m, true);
6,976✔
379
        if (r < 0)
6,976✔
380
                return r;
381

382
        signature = sd_bus_message_get_signature(m, true);
6,976✔
383
        if (!signature)
6,976✔
384
                return -EINVAL;
385

386
        if (!streq(strempty(c->vtable->x.method.signature), signature))
7,880✔
387
                return sd_bus_reply_method_errorf(
1✔
388
                                m,
389
                                SD_BUS_ERROR_INVALID_ARGS,
390
                                "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
391
                                signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
1✔
392

393
        /* Keep track what the signature of the reply to this message
394
         * should be, so that this can be enforced when sealing the
395
         * reply. */
396
        m->enforced_reply_signature = strempty(c->vtable->x.method.result);
6,975✔
397

398
        if (c->vtable->x.method.handler) {
6,975✔
399
                sd_bus_slot *slot;
6,974✔
400

401
                slot = container_of(c->parent, sd_bus_slot, node_vtable);
6,974✔
402

403
                bus->current_slot = sd_bus_slot_ref(slot);
6,974✔
404
                bus->current_handler = c->vtable->x.method.handler;
6,974✔
405
                bus->current_userdata = u;
6,974✔
406
                r = c->vtable->x.method.handler(m, u, &error);
6,974✔
407
                bus->current_userdata = NULL;
6,919✔
408
                bus->current_handler = NULL;
6,919✔
409
                bus->current_slot = sd_bus_slot_unref(slot);
6,919✔
410

411
                return bus_maybe_reply_error(m, r, &error);
6,919✔
412
        }
413

414
        /* If the method callback is NULL, make this a successful NOP */
415
        r = sd_bus_reply_method_return(m, NULL);
1✔
416
        if (r < 0)
1✔
417
                return r;
×
418

419
        return 1;
420
}
421

422
static int invoke_property_get(
1,674,939✔
423
                sd_bus *bus,
424
                sd_bus_slot *slot,
425
                const sd_bus_vtable *v,
426
                const char *path,
427
                const char *interface,
428
                const char *property,
429
                sd_bus_message *reply,
430
                void *userdata,
431
                sd_bus_error *error) {
432

433
        const void *p;
1,674,939✔
434
        int r;
1,674,939✔
435

436
        assert(bus);
1,674,939✔
437
        assert(slot);
1,674,939✔
438
        assert(v);
1,674,939✔
439
        assert(path);
1,674,939✔
440
        assert(interface);
1,674,939✔
441
        assert(property);
1,674,939✔
442
        assert(reply);
1,674,939✔
443

444
        if (v->x.property.get) {
1,674,939✔
445

446
                bus->current_slot = sd_bus_slot_ref(slot);
790,635✔
447
                bus->current_userdata = userdata;
790,635✔
448
                r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
790,635✔
449
                bus->current_userdata = NULL;
790,635✔
450
                bus->current_slot = sd_bus_slot_unref(slot);
790,635✔
451

452
                if (r < 0)
790,635✔
453
                        return r;
454
                if (sd_bus_error_is_set(error))
790,635✔
455
                        return -sd_bus_error_get_errno(error);
×
456
                return r;
457
        }
458

459
        /* Automatic handling if no callback is defined. */
460

461
        if (streq(v->x.property.signature, "as"))
884,304✔
462
                return sd_bus_message_append_strv(reply, *(char***) userdata);
17,187✔
463

464
        assert(signature_is_single(v->x.property.signature, false));
867,117✔
465
        assert(bus_type_is_basic(v->x.property.signature[0]));
867,117✔
466

467
        switch (v->x.property.signature[0]) {
867,117✔
468

469
        case SD_BUS_TYPE_STRING:
66,796✔
470
        case SD_BUS_TYPE_SIGNATURE:
471
                p = strempty(*(char**) userdata);
66,796✔
472
                break;
473

474
        case SD_BUS_TYPE_OBJECT_PATH:
×
475
                p = *(char**) userdata;
×
476
                assert(p);
×
477
                break;
478

479
        default:
480
                p = userdata;
481
                break;
482
        }
483

484
        return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
867,117✔
485
}
486

487
static int invoke_property_set(
31✔
488
                sd_bus *bus,
489
                sd_bus_slot *slot,
490
                const sd_bus_vtable *v,
491
                const char *path,
492
                const char *interface,
493
                const char *property,
494
                sd_bus_message *value,
495
                void *userdata,
496
                sd_bus_error *error) {
497

498
        int r;
31✔
499

500
        assert(bus);
31✔
501
        assert(slot);
31✔
502
        assert(v);
31✔
503
        assert(path);
31✔
504
        assert(interface);
31✔
505
        assert(property);
31✔
506
        assert(value);
31✔
507

508
        if (v->x.property.set) {
31✔
509

510
                bus->current_slot = sd_bus_slot_ref(slot);
29✔
511
                bus->current_userdata = userdata;
29✔
512
                r = v->x.property.set(bus, path, interface, property, value, userdata, error);
29✔
513
                bus->current_userdata = NULL;
29✔
514
                bus->current_slot = sd_bus_slot_unref(slot);
29✔
515

516
                if (r < 0)
29✔
517
                        return r;
518
                if (sd_bus_error_is_set(error))
29✔
519
                        return -sd_bus_error_get_errno(error);
×
520
                return r;
521
        }
522

523
        /*  Automatic handling if no callback is defined. */
524

525
        assert(signature_is_single(v->x.property.signature, false));
2✔
526
        assert(bus_type_is_basic(v->x.property.signature[0]));
2✔
527

528
        switch (v->x.property.signature[0]) {
2✔
529

530
        case SD_BUS_TYPE_STRING:
1✔
531
        case SD_BUS_TYPE_OBJECT_PATH:
532
        case SD_BUS_TYPE_SIGNATURE: {
533
                const char *p;
1✔
534
                char *n;
1✔
535

536
                r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
1✔
537
                if (r < 0)
1✔
538
                        return r;
×
539

540
                n = strdup(p);
1✔
541
                if (!n)
1✔
542
                        return -ENOMEM;
543

544
                free(*(char**) userdata);
1✔
545
                *(char**) userdata = n;
1✔
546

547
                break;
1✔
548
        }
549

550
        default:
1✔
551
                r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
1✔
552
                if (r < 0)
1✔
553
                        return r;
×
554

555
                break;
556
        }
557

558
        return 1;
559
}
560

561
static int property_get_set_callbacks_run(
13,465✔
562
                sd_bus *bus,
563
                sd_bus_message *m,
564
                struct vtable_member *c,
565
                bool require_fallback,
566
                bool is_get,
567
                bool *found_object) {
568

569
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
570
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
13,465✔
571
        sd_bus_slot *slot;
13,465✔
572
        void *u = NULL;
13,465✔
573
        int r;
13,465✔
574

575
        assert(bus);
13,465✔
576
        assert(m);
13,465✔
577
        assert(c);
13,465✔
578
        assert(found_object);
13,465✔
579

580
        if (require_fallback && !c->parent->is_fallback)
13,465✔
581
                return 0;
582

583
        if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
13,465✔
584
                r = sd_bus_message_sensitive(m);
×
585
                if (r < 0)
×
586
                        return r;
587
        }
588

589
        r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
13,465✔
590
        if (r <= 0)
13,465✔
591
                return bus_maybe_reply_error(m, r, &error);
1,111✔
592
        if (bus->nodes_modified)
12,354✔
593
                return 0;
594

595
        slot = container_of(c->parent, sd_bus_slot, node_vtable);
12,354✔
596

597
        *found_object = true;
12,354✔
598

599
        r = sd_bus_message_new_method_return(m, &reply);
12,354✔
600
        if (r < 0)
12,354✔
601
                return r;
602

603
        if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
12,354✔
604
                r = sd_bus_message_sensitive(reply);
×
605
                if (r < 0)
×
606
                        return r;
607
        }
608

609
        if (is_get) {
12,354✔
610
                /* Note that we do not protect against reexecution
611
                 * here (using the last_iteration check, see below),
612
                 * should the node tree have changed and we got called
613
                 * again. We assume that property Get() calls are
614
                 * ultimately without side-effects or if they aren't
615
                 * then at least idempotent. */
616

617
                r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
12,323✔
618
                if (r < 0)
12,323✔
619
                        return r;
620

621
                /* Note that we do not do an access check here. Read
622
                 * access to properties is always unrestricted, since
623
                 * PropertiesChanged signals broadcast contents
624
                 * anyway. */
625

626
                r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
12,323✔
627
                if (r < 0)
12,323✔
628
                        return bus_maybe_reply_error(m, r, &error);
×
629

630
                if (bus->nodes_modified)
12,323✔
631
                        return 0;
632

633
                r = sd_bus_message_close_container(reply);
12,323✔
634
                if (r < 0)
12,323✔
635
                        return r;
636

637
        } else {
638
                const char *signature = NULL;
31✔
639
                char type = 0;
31✔
640

641
                if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
31✔
642
                        return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
×
643

644
                /* Avoid that we call the set routine more than once
645
                 * if the processing of this message got restarted
646
                 * because the node tree changed. */
647
                if (c->last_iteration == bus->iteration_counter)
31✔
648
                        return 0;
649

650
                c->last_iteration = bus->iteration_counter;
31✔
651

652
                r = sd_bus_message_peek_type(m, &type, &signature);
31✔
653
                if (r < 0)
31✔
654
                        return r;
655

656
                if (type != 'v')
31✔
657
                        return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_SIGNATURE,
×
658
                                                          "Incorrect signature when setting property '%s', expected 'v', got '%c'.",
659
                                                          c->member, type);
660
                if (!streq(strempty(signature), strempty(c->vtable->x.property.signature)))
31✔
661
                        return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS,
×
662
                                                          "Incorrect parameters for property '%s', expected '%s', got '%s'.",
663
                                                          c->member, strempty(c->vtable->x.property.signature), strempty(signature));
×
664

665
                r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
31✔
666
                if (r < 0)
31✔
667
                        return r;
668

669
                r = check_access(bus, m, c, &error);
31✔
670
                if (r < 0)
31✔
671
                        return bus_maybe_reply_error(m, r, &error);
×
672

673
                r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
31✔
674
                if (r < 0)
31✔
675
                        return bus_maybe_reply_error(m, r, &error);
×
676

677
                if (bus->nodes_modified)
31✔
678
                        return 0;
679

680
                r = sd_bus_message_exit_container(m);
31✔
681
                if (r < 0)
31✔
682
                        return r;
683
        }
684

685
        r = sd_bus_send(bus, reply, NULL);
12,354✔
686
        if (r < 0)
12,354✔
687
                return r;
×
688

689
        return 1;
690
}
691

692
static int vtable_append_one_property(
1,662,616✔
693
                sd_bus *bus,
694
                sd_bus_message *reply,
695
                const char *path,
696
                struct node_vtable *c,
697
                const sd_bus_vtable *v,
698
                void *userdata,
699
                sd_bus_error *error) {
700

701
        sd_bus_slot *slot;
1,662,616✔
702
        int r;
1,662,616✔
703

704
        assert(bus);
1,662,616✔
705
        assert(reply);
1,662,616✔
706
        assert(path);
1,662,616✔
707
        assert(c);
1,662,616✔
708
        assert(v);
1,662,616✔
709

710
        if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
1,662,616✔
711
                r = sd_bus_message_sensitive(reply);
×
712
                if (r < 0)
×
713
                        return r;
714
        }
715

716
        r = sd_bus_message_open_container(reply, 'e', "sv");
1,662,616✔
717
        if (r < 0)
1,662,616✔
718
                return r;
719

720
        r = sd_bus_message_append(reply, "s", v->x.property.member);
1,662,616✔
721
        if (r < 0)
1,662,616✔
722
                return r;
723

724
        r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
1,662,616✔
725
        if (r < 0)
1,662,616✔
726
                return r;
727

728
        slot = container_of(c, sd_bus_slot, node_vtable);
1,662,616✔
729

730
        r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
1,662,616✔
731
        if (r < 0)
1,662,616✔
732
                return r;
733
        if (bus->nodes_modified)
1,662,616✔
734
                return 0;
735

736
        r = sd_bus_message_close_container(reply);
1,662,616✔
737
        if (r < 0)
1,662,616✔
738
                return r;
739

740
        r = sd_bus_message_close_container(reply);
1,662,616✔
741
        if (r < 0)
1,662,616✔
742
                return r;
×
743

744
        return 0;
745
}
746

747
static int vtable_append_all_properties(
7,640✔
748
                sd_bus *bus,
749
                sd_bus_message *reply,
750
                const char *path,
751
                struct node_vtable *c,
752
                void *userdata,
753
                sd_bus_error *error) {
754

755
        const sd_bus_vtable *v;
7,640✔
756
        int r;
7,640✔
757

758
        assert(bus);
7,640✔
759
        assert(reply);
7,640✔
760
        assert(path);
7,640✔
761
        assert(c);
7,640✔
762

763
        if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
7,640✔
764
                return 1;
765

766
        v = c->vtable;
7,640✔
767
        for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
588,030✔
768
                if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
580,390✔
769
                        continue;
33,404✔
770

771
                if (v->flags & SD_BUS_VTABLE_HIDDEN)
546,986✔
772
                        continue;
20,339✔
773

774
                /* Let's not include properties marked as "explicit" in any message that contains a generic
775
                 * dump of properties, but only in those generated as a response to an explicit request. */
776
                if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
526,647✔
777
                        continue;
36✔
778

779
                /* Let's not include properties marked only for invalidation on change (i.e. in contrast to
780
                 * those whose new values are included in PropertiesChanges message) in any signals. This is
781
                 * useful to ensure they aren't included in InterfacesAdded messages. */
782
                if (reply->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
526,611✔
783
                    FLAGS_SET(v->flags, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
32✔
784
                        continue;
7✔
785

786
                r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
526,604✔
787
                if (r < 0)
526,604✔
788
                        return r;
789
                if (bus->nodes_modified)
526,604✔
790
                        return 0;
791
        }
792

793
        return 1;
794
}
795

796
static int property_get_all_callbacks_run(
1,788✔
797
                sd_bus *bus,
798
                sd_bus_message *m,
799
                struct node_vtable *first,
800
                bool require_fallback,
801
                const char *iface,
802
                bool *found_object) {
803

804
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1,788✔
805
        bool found_interface;
1,788✔
806
        int r;
1,788✔
807

808
        assert(bus);
1,788✔
809
        assert(m);
1,788✔
810
        assert(found_object);
1,788✔
811

812
        r = sd_bus_message_new_method_return(m, &reply);
1,788✔
813
        if (r < 0)
1,788✔
814
                return r;
815

816
        r = sd_bus_message_open_container(reply, 'a', "{sv}");
1,788✔
817
        if (r < 0)
1,788✔
818
                return r;
819

820
        found_interface = !iface || STR_IN_SET(iface,
1,788✔
821
                                               "org.freedesktop.DBus.Properties",
822
                                               "org.freedesktop.DBus.Peer",
823
                                               "org.freedesktop.DBus.Introspectable");
824

825
        LIST_FOREACH(vtables, c, first) {
45,784✔
826
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
43,996✔
827
                void *u;
43,996✔
828

829
                if (require_fallback && !c->is_fallback)
43,996✔
830
                        continue;
×
831

832
                r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
43,996✔
833
                if (r < 0)
43,996✔
834
                        return bus_maybe_reply_error(m, r, &error);
×
835
                if (bus->nodes_modified)
43,996✔
836
                        return 0;
837
                if (r == 0)
43,996✔
838
                        continue;
36,395✔
839

840
                *found_object = true;
7,601✔
841

842
                if (iface && !streq(c->interface, iface))
7,601✔
843
                        continue;
1✔
844
                found_interface = true;
7,600✔
845

846
                r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
7,600✔
847
                if (r < 0)
7,600✔
848
                        return bus_maybe_reply_error(m, r, &error);
×
849
                if (bus->nodes_modified)
7,600✔
850
                        return 0;
851
        }
852

853
        if (!*found_object)
1,788✔
854
                return 0;
855

856
        if (!found_interface) {
1,787✔
857
                r = sd_bus_reply_method_errorf(
1✔
858
                                m,
859
                                SD_BUS_ERROR_UNKNOWN_INTERFACE,
860
                                "Unknown interface '%s'.", iface);
861
                if (r < 0)
1✔
862
                        return r;
863

864
                return 1;
1✔
865
        }
866

867
        r = sd_bus_message_close_container(reply);
1,786✔
868
        if (r < 0)
1,786✔
869
                return r;
870

871
        r = sd_bus_send(bus, reply, NULL);
1,786✔
872
        if (r < 0)
1,786✔
873
                return r;
×
874

875
        return 1;
876
}
877

878
static int bus_node_exists(
1,271✔
879
                sd_bus *bus,
880
                struct node *n,
881
                const char *path,
882
                bool require_fallback) {
883

884
        int r;
1,271✔
885

886
        assert(bus);
1,271✔
887
        assert(n);
1,271✔
888
        assert(path);
1,271✔
889

890
        /* Tests if there's anything attached directly to this node
891
         * for the specified path */
892

893
        if (!require_fallback && (n->enumerators || n->object_managers))
1,271✔
894
                return true;
895

896
        LIST_FOREACH(callbacks, k, n->callbacks) {
1,113✔
897
                if (require_fallback && !k->is_fallback)
×
898
                        continue;
×
899

900
                return 1;
901
        }
902

903
        LIST_FOREACH(vtables, c, n->vtables) {
4,020✔
904
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
4,020✔
905

906
                if (require_fallback && !c->is_fallback)
4,020✔
907
                        continue;
×
908

909
                r = node_vtable_get_userdata(bus, path, c, NULL, &error);
4,020✔
910
                if (r != 0)
4,020✔
911
                        return r;
912
                if (bus->nodes_modified)
2,907✔
913
                        return 0;
914
        }
915

916
        return 0;
917
}
918

919
int introspect_path(
2,075✔
920
                sd_bus *bus,
921
                const char *path,
922
                struct node *n,
923
                bool require_fallback,
924
                bool ignore_nodes_modified,
925
                bool *found_object,
926
                char **ret,
927
                sd_bus_error *error) {
928

929
        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
2,075✔
930
        _cleanup_(introspect_done) struct introspect intro = {};
2,075✔
931
        bool empty;
2,075✔
932
        int r;
2,075✔
933

934
        if (!n) {
2,075✔
935
                n = hashmap_get(bus->nodes, path);
2✔
936
                if (!n)
2✔
937
                        return -ENOENT;
938
        }
939

940
        r = get_child_nodes(bus, path, n, 0, &s, error);
2,075✔
941
        if (r < 0)
2,075✔
942
                return r;
943
        if (bus->nodes_modified && !ignore_nodes_modified)
2,075✔
944
                return 0;
945

946
        r = introspect_begin(&intro, bus->trusted);
2,075✔
947
        if (r < 0)
2,075✔
948
                return r;
949

950
        r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
4,147✔
951
        if (r < 0)
2,075✔
952
                return r;
953

954
        empty = ordered_set_isempty(s);
2,075✔
955

956
        LIST_FOREACH(vtables, c, n->vtables) {
3,215✔
957
                if (require_fallback && !c->is_fallback)
1,140✔
958
                        continue;
×
959

960
                r = node_vtable_get_userdata(bus, path, c, NULL, error);
1,140✔
961
                if (r < 0)
1,140✔
962
                        return r;
963
                if (bus->nodes_modified && !ignore_nodes_modified)
1,140✔
964
                        return 0;
965
                if (r == 0)
1,140✔
966
                        continue;
328✔
967

968
                empty = false;
812✔
969

970
                if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
812✔
971
                        continue;
×
972

973
                r = introspect_write_interface(&intro, c->interface, c->vtable);
812✔
974
                if (r < 0)
812✔
975
                        return r;
976
        }
977

978
        if (empty) {
2,075✔
979
                /* Nothing?, let's see if we exist at all, and if not
980
                 * refuse to do anything */
981
                r = bus_node_exists(bus, n, path, require_fallback);
155✔
982
                if (r <= 0)
155✔
983
                        return r;
984
                if (bus->nodes_modified && !ignore_nodes_modified)
155✔
985
                        return 0;
986
        }
987

988
        if (found_object)
2,075✔
989
                *found_object = true;
2,073✔
990

991
        r = introspect_write_child_nodes(&intro, s, path);
2,075✔
992
        if (r < 0)
2,075✔
993
                return r;
994

995
        r = introspect_finish(&intro, ret);
2,075✔
996
        if (r < 0)
2,075✔
997
                return r;
×
998

999
        return 1;
1000
}
1001

1002
static int process_introspect(
2,073✔
1003
                sd_bus *bus,
1004
                sd_bus_message *m,
1005
                struct node *n,
1006
                bool require_fallback,
1007
                bool *found_object) {
1008

1009
        _cleanup_free_ char *s = NULL;
2,073✔
1010
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1011
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2,073✔
1012
        int r;
2,073✔
1013

1014
        assert(bus);
2,073✔
1015
        assert(m);
2,073✔
1016
        assert(n);
2,073✔
1017
        assert(found_object);
2,073✔
1018

1019
        r = introspect_path(bus, m->path, n, require_fallback, false, found_object, &s, &error);
2,073✔
1020
        if (r < 0)
2,073✔
1021
                return bus_maybe_reply_error(m, r, &error);
×
1022
        if (r == 0)
2,073✔
1023
                /* nodes_modified == true */
1024
                return 0;
1025

1026
        r = sd_bus_message_new_method_return(m, &reply);
2,073✔
1027
        if (r < 0)
2,073✔
1028
                return r;
1029

1030
        r = sd_bus_message_append(reply, "s", s);
2,073✔
1031
        if (r < 0)
2,073✔
1032
                return r;
1033

1034
        r = sd_bus_send(bus, reply, NULL);
2,073✔
1035
        if (r < 0)
2,073✔
1036
                return r;
×
1037

1038
        return 1;
1039
}
1040

1041
static int object_manager_serialize_path(
129✔
1042
                sd_bus *bus,
1043
                sd_bus_message *reply,
1044
                const char *prefix,
1045
                const char *path,
1046
                bool require_fallback,
1047
                bool *found_object_manager,
1048
                sd_bus_error *error) {
1049

1050
        const char *previous_interface = NULL;
129✔
1051
        bool found_something = false;
129✔
1052
        struct node *n;
129✔
1053
        int r;
129✔
1054

1055
        assert(bus);
129✔
1056
        assert(reply);
129✔
1057
        assert(prefix);
129✔
1058
        assert(path);
129✔
1059
        assert(found_object_manager);
129✔
1060
        assert(error);
129✔
1061

1062
        n = hashmap_get(bus->nodes, prefix);
129✔
1063
        if (!n)
129✔
1064
                return 0;
1065

1066
        if (!require_fallback && n->object_managers)
98✔
1067
                *found_object_manager = true;
1✔
1068

1069
        LIST_FOREACH(vtables, i, n->vtables) {
131✔
1070
                void *u;
33✔
1071

1072
                if (require_fallback && !i->is_fallback)
33✔
1073
                        continue;
×
1074

1075
                r = node_vtable_get_userdata(bus, path, i, &u, error);
33✔
1076
                if (r < 0)
33✔
1077
                        return r;
×
1078
                if (bus->nodes_modified)
33✔
1079
                        return 0;
1080
                if (r == 0)
33✔
1081
                        continue;
×
1082

1083
                if (!found_something) {
33✔
1084

1085
                        /* Open the object part */
1086

1087
                        r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
33✔
1088
                        if (r < 0)
33✔
1089
                                return r;
1090

1091
                        r = sd_bus_message_append(reply, "o", path);
33✔
1092
                        if (r < 0)
33✔
1093
                                return r;
1094

1095
                        r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
33✔
1096
                        if (r < 0)
33✔
1097
                                return r;
1098

1099
                        r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
33✔
1100
                        if (r < 0)
33✔
1101
                                return r;
1102

1103
                        r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
33✔
1104
                        if (r < 0)
33✔
1105
                                return r;
1106

1107
                        r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
33✔
1108
                        if (r < 0)
33✔
1109
                                return r;
1110

1111
                        if (*found_object_manager) {
33✔
1112
                                r = sd_bus_message_append(
1✔
1113
                                                reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1114
                                if (r < 0)
1✔
1115
                                        return r;
1116
                        }
1117

1118
                        found_something = true;
1119
                }
1120

1121
                if (!streq_ptr(previous_interface, i->interface)) {
33✔
1122

1123
                        /* Maybe close the previous interface part */
1124

1125
                        if (previous_interface) {
33✔
1126
                                r = sd_bus_message_close_container(reply);
×
1127
                                if (r < 0)
×
1128
                                        return r;
1129

1130
                                r = sd_bus_message_close_container(reply);
×
1131
                                if (r < 0)
×
1132
                                        return r;
1133
                        }
1134

1135
                        /* Open the new interface part */
1136

1137
                        r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
33✔
1138
                        if (r < 0)
33✔
1139
                                return r;
1140

1141
                        r = sd_bus_message_append(reply, "s", i->interface);
33✔
1142
                        if (r < 0)
33✔
1143
                                return r;
1144

1145
                        r = sd_bus_message_open_container(reply, 'a', "{sv}");
33✔
1146
                        if (r < 0)
33✔
1147
                                return r;
1148
                }
1149

1150
                r = vtable_append_all_properties(bus, reply, path, i, u, error);
33✔
1151
                if (r < 0)
33✔
1152
                        return r;
1153
                if (bus->nodes_modified)
33✔
1154
                        return 0;
1155

1156
                previous_interface = i->interface;
33✔
1157
        }
1158

1159
        if (previous_interface) {
98✔
1160
                r = sd_bus_message_close_container(reply);
33✔
1161
                if (r < 0)
33✔
1162
                        return r;
1163

1164
                r = sd_bus_message_close_container(reply);
33✔
1165
                if (r < 0)
33✔
1166
                        return r;
1167
        }
1168

1169
        if (found_something) {
98✔
1170
                r = sd_bus_message_close_container(reply);
33✔
1171
                if (r < 0)
33✔
1172
                        return r;
1173

1174
                r = sd_bus_message_close_container(reply);
33✔
1175
                if (r < 0)
33✔
1176
                        return r;
×
1177
        }
1178

1179
        return 1;
1180
}
1181

1182
static int object_manager_serialize_path_and_fallbacks(
33✔
1183
                sd_bus *bus,
1184
                sd_bus_message *reply,
1185
                const char *path,
1186
                sd_bus_error *error) {
1187

1188
        _cleanup_free_ char *prefix = NULL;
33✔
1189
        size_t pl;
33✔
1190
        int r;
33✔
1191
        bool found_object_manager = false;
33✔
1192

1193
        assert(bus);
33✔
1194
        assert(reply);
33✔
1195
        assert(path);
33✔
1196
        assert(error);
33✔
1197

1198
        /* First, add all vtables registered for this path */
1199
        r = object_manager_serialize_path(bus, reply, path, path, false, &found_object_manager, error);
33✔
1200
        if (r < 0)
33✔
1201
                return r;
1202
        if (bus->nodes_modified)
33✔
1203
                return 0;
1204

1205
        /* Second, add fallback vtables registered for any of the prefixes */
1206
        pl = strlen(path);
33✔
1207
        assert(pl <= BUS_PATH_SIZE_MAX);
33✔
1208
        prefix = new(char, pl + 1);
33✔
1209
        if (!prefix)
33✔
1210
                return -ENOMEM;
1211

1212
        OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
258✔
1213
                r = object_manager_serialize_path(bus, reply, prefix, path, true, &found_object_manager, error);
96✔
1214
                if (r < 0)
96✔
1215
                        return r;
1216
                if (bus->nodes_modified)
96✔
1217
                        return 0;
1218
        }
1219

1220
        return 0;
1221
}
1222

1223
static int process_get_managed_objects(
3✔
1224
                sd_bus *bus,
1225
                sd_bus_message *m,
1226
                struct node *n,
1227
                bool require_fallback,
1228
                bool *found_object) {
1229

1230
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1231
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3✔
1232
        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
3✔
1233
        char *path;
3✔
1234
        int r;
3✔
1235

1236
        assert(bus);
3✔
1237
        assert(m);
3✔
1238
        assert(n);
3✔
1239
        assert(found_object);
3✔
1240

1241
        /* Spec says, GetManagedObjects() is only implemented on the root of a
1242
         * sub-tree. Therefore, we require a registered object-manager on
1243
         * exactly the queried path, otherwise, we refuse to respond. */
1244

1245
        if (require_fallback || !n->object_managers)
3✔
1246
                return 0;
1247

1248
        r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1✔
1249
        if (r < 0)
1✔
1250
                return bus_maybe_reply_error(m, r, &error);
×
1251
        if (bus->nodes_modified)
1✔
1252
                return 0;
1253

1254
        r = sd_bus_message_new_method_return(m, &reply);
1✔
1255
        if (r < 0)
1✔
1256
                return r;
1257

1258
        r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1✔
1259
        if (r < 0)
1✔
1260
                return r;
1261

1262
        ORDERED_SET_FOREACH(path, s) {
34✔
1263
                r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
33✔
1264
                if (r < 0)
33✔
1265
                        return bus_maybe_reply_error(m, r, &error);
×
1266

1267
                if (bus->nodes_modified)
33✔
1268
                        return 0;
1269
        }
1270

1271
        r = sd_bus_message_close_container(reply);
1✔
1272
        if (r < 0)
1✔
1273
                return r;
1274

1275
        r = sd_bus_send(bus, reply, NULL);
1✔
1276
        if (r < 0)
1✔
1277
                return r;
×
1278

1279
        return 1;
1280
}
1281

1282
static int object_find_and_run(
43,779✔
1283
                sd_bus *bus,
1284
                sd_bus_message *m,
1285
                const char *p,
1286
                bool require_fallback,
1287
                bool *found_object) {
1288

1289
        struct node *n;
43,779✔
1290
        struct vtable_member vtable_key, *v;
43,779✔
1291
        int r;
43,779✔
1292

1293
        assert(bus);
43,779✔
1294
        assert(m);
43,779✔
1295
        assert(p);
43,779✔
1296
        assert(found_object);
43,779✔
1297

1298
        n = hashmap_get(bus->nodes, p);
43,779✔
1299
        if (!n)
43,779✔
1300
                return 0;
43,724✔
1301

1302
        /* First, try object callbacks */
1303
        r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
29,124✔
1304
        if (r != 0)
29,124✔
1305
                return r;
1306
        if (bus->nodes_modified)
29,124✔
1307
                return 0;
1308

1309
        if (!m->interface || !m->member)
29,124✔
1310
                return 0;
1311

1312
        /* Then, look for a known method */
1313
        vtable_key.path = (char*) p;
29,124✔
1314
        vtable_key.interface = m->interface;
29,124✔
1315
        vtable_key.member = m->member;
29,124✔
1316

1317
        v = set_get(bus->vtable_methods, &vtable_key);
29,124✔
1318
        if (v) {
29,124✔
1319
                r = method_callbacks_run(bus, m, v, require_fallback, found_object);
6,976✔
1320
                if (r != 0)
6,921✔
1321
                        return r;
1322
                if (bus->nodes_modified)
125✔
1323
                        return 0;
1324
        }
1325

1326
        /* Then, look for a known property */
1327
        if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
22,273✔
1328
                bool get = false;
19,693✔
1329

1330
                get = streq(m->member, "Get");
19,693✔
1331

1332
                if (get || streq(m->member, "Set")) {
19,693✔
1333

1334
                        r = sd_bus_message_rewind(m, true);
17,905✔
1335
                        if (r < 0)
17,905✔
1336
                                return r;
1337

1338
                        vtable_key.path = (char*) p;
17,905✔
1339

1340
                        r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
17,905✔
1341
                        if (r < 0)
17,905✔
1342
                                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
×
1343

1344
                        v = set_get(bus->vtable_properties, &vtable_key);
17,905✔
1345
                        if (v) {
17,905✔
1346
                                r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
13,465✔
1347
                                if (r != 0)
13,465✔
1348
                                        return r;
1349
                        }
1350

1351
                } else if (streq(m->member, "GetAll")) {
1,788✔
1352
                        const char *iface;
1,788✔
1353

1354
                        r = sd_bus_message_rewind(m, true);
1,788✔
1355
                        if (r < 0)
1,788✔
1356
                                return r;
1,787✔
1357

1358
                        r = sd_bus_message_read(m, "s", &iface);
1,788✔
1359
                        if (r < 0)
1,788✔
1360
                                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
×
1361

1362
                        if (iface[0] == 0)
1,788✔
1363
                                iface = NULL;
1,786✔
1364

1365
                        r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1,788✔
1366
                        if (r != 0)
1,788✔
1367
                                return r;
1368
                }
1369

1370
        } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
2,580✔
1371

1372
                if (!isempty(sd_bus_message_get_signature(m, true)))
2,073✔
1373
                        return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
×
1374

1375
                r = process_introspect(bus, m, n, require_fallback, found_object);
2,073✔
1376
                if (r != 0)
2,073✔
1377
                        return r;
1378

1379
        } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
507✔
1380

1381
                if (!isempty(sd_bus_message_get_signature(m, true)))
3✔
1382
                        return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
×
1383

1384
                r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
3✔
1385
                if (r != 0)
3✔
1386
                        return r;
1387
        }
1388

1389
        if (bus->nodes_modified)
6,057✔
1390
                return 0;
1391

1392
        if (!*found_object) {
6,057✔
1393
                r = bus_node_exists(bus, n, m->path, require_fallback);
1,116✔
1394
                if (r < 0)
1,116✔
1395
                        return bus_maybe_reply_error(m, r, NULL);
×
1396
                if (bus->nodes_modified)
1,116✔
1397
                        return 0;
1398
                if (r > 0)
1,116✔
1399
                        *found_object = true;
1,116✔
1400
        }
1401

1402
        return 0;
1403
}
1404

1405
int bus_process_object(sd_bus *bus, sd_bus_message *m) {
520,705✔
1406
        _cleanup_free_ char *prefix = NULL;
520,650✔
1407
        int r;
520,705✔
1408
        size_t pl;
520,705✔
1409
        bool found_object = false;
520,705✔
1410

1411
        assert(bus);
520,705✔
1412
        assert(m);
520,705✔
1413

1414
        if (bus->is_monitor)
520,705✔
1415
                return 0;
1416

1417
        if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
520,701✔
1418
                return 0;
1419

1420
        if (hashmap_isempty(bus->nodes))
24,740✔
1421
                return 0;
1422

1423
        /* Never respond to broadcast messages */
1424
        if (bus->bus_client && !m->destination)
24,305✔
1425
                return 0;
1426

1427
        assert(m->path);
24,305✔
1428
        assert(m->member);
24,305✔
1429

1430
        pl = strlen(m->path);
24,305✔
1431
        assert(pl <= BUS_PATH_SIZE_MAX);
24,305✔
1432
        prefix = new(char, pl + 1);
24,305✔
1433
        if (!prefix)
24,305✔
1434
                return -ENOMEM;
1435

1436
        do {
24,305✔
1437
                bus->nodes_modified = false;
24,305✔
1438

1439
                r = object_find_and_run(bus, m, m->path, false, &found_object);
24,305✔
1440
                if (r != 0)
24,250✔
1441
                        return r;
1442

1443
                /* Look for fallback prefixes */
1444
                OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
41,424✔
1445

1446
                        if (bus->nodes_modified)
19,474✔
1447
                                break;
1448

1449
                        r = object_find_and_run(bus, m, prefix, true, &found_object);
19,474✔
1450
                        if (r != 0)
19,474✔
1451
                                return r;
1452
                }
1453

1454
        } while (bus->nodes_modified);
1,238✔
1455

1456
        if (!found_object)
1,238✔
1457
                return 0;
1458

1459
        if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1,366✔
1460
            sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) {
128✔
1461
                const char *interface = NULL, *property = NULL;
1,110✔
1462

1463
                (void) sd_bus_message_rewind(m, true);
1,110✔
1464
                (void) sd_bus_message_read_basic(m, 's', &interface);
1,110✔
1465
                (void) sd_bus_message_read_basic(m, 's', &property);
1,110✔
1466

1467
                r = sd_bus_reply_method_errorf(
1,110✔
1468
                                m,
1469
                                SD_BUS_ERROR_UNKNOWN_PROPERTY,
1470
                                "Unknown interface %s or property %s.", strnull(interface), strnull(property));
1471
        } else
1472
                r = sd_bus_reply_method_errorf(
128✔
1473
                                m,
1474
                                SD_BUS_ERROR_UNKNOWN_METHOD,
1475
                                "Unknown method %s or interface %s.", m->member, m->interface);
1476

1477
        if (r < 0)
1,238✔
1478
                return r;
1✔
1479

1480
        return 1;
1481
}
1482

1483
static struct node* bus_node_allocate(sd_bus *bus, const char *path) {
58,902✔
1484
        struct node *n, *parent;
58,902✔
1485
        const char *e;
58,902✔
1486
        _cleanup_free_ char *s = NULL;
58,902✔
1487
        char *p;
58,902✔
1488
        int r;
58,902✔
1489

1490
        assert(bus);
58,902✔
1491
        assert(path);
58,902✔
1492
        assert(path[0] == '/');
58,902✔
1493

1494
        n = hashmap_get(bus->nodes, path);
58,902✔
1495
        if (n)
58,902✔
1496
                return n;
1497

1498
        r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
16,212✔
1499
        if (r < 0)
16,212✔
1500
                return NULL;
1501

1502
        s = strdup(path);
16,212✔
1503
        if (!s)
16,212✔
1504
                return NULL;
1505

1506
        if (streq(path, "/"))
16,212✔
1507
                parent = NULL;
1508
        else {
1509
                assert_se(e = strrchr(path, '/'));
13,699✔
1510

1511
                p = strndupa_safe(path, MAX(1, e - path));
13,699✔
1512

1513
                parent = bus_node_allocate(bus, p);
13,699✔
1514
                if (!parent)
13,699✔
1515
                        return NULL;
1516
        }
1517

1518
        n = new0(struct node, 1);
16,212✔
1519
        if (!n)
16,212✔
1520
                return NULL;
1521

1522
        n->parent = parent;
16,212✔
1523
        n->path = TAKE_PTR(s);
16,212✔
1524

1525
        r = hashmap_put(bus->nodes, n->path, n);
16,212✔
1526
        if (r < 0) {
16,212✔
1527
                free(n->path);
×
1528
                return mfree(n);
×
1529
        }
1530

1531
        if (parent)
16,212✔
1532
                LIST_PREPEND(siblings, parent->child, n);
13,699✔
1533

1534
        return n;
1535
}
1536

1537
void bus_node_gc(sd_bus *b, struct node *n) {
60,865✔
1538
        assert(b);
60,865✔
1539

1540
        if (!n)
60,865✔
1541
                return;
1542

1543
        if (n->child ||
58,407✔
1544
            n->callbacks ||
54,337✔
1545
            n->vtables ||
54,337✔
1546
            n->enumerators ||
15,883✔
1547
            n->object_managers)
15,882✔
1548
                return;
1549

1550
        assert_se(hashmap_remove(b->nodes, n->path) == n);
15,882✔
1551

1552
        if (n->parent)
15,882✔
1553
                LIST_REMOVE(siblings, n->parent->child, n);
13,424✔
1554

1555
        free(n->path);
15,882✔
1556
        bus_node_gc(b, n->parent);
15,882✔
1557
        free(n);
15,882✔
1558
}
1559

1560
static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path, bool* path_has_object_manager) {
12✔
1561
        struct node *n;
12✔
1562

1563
        assert(bus);
12✔
1564
        assert(path);
12✔
1565
        assert(path_has_object_manager);
12✔
1566

1567
        n = hashmap_get(bus->nodes, path);
12✔
1568

1569
        if (n)
12✔
1570
                *path_has_object_manager = n->object_managers;
1✔
1571

1572
        if (!n) {
1✔
1573
                _cleanup_free_ char *prefix = NULL;
11✔
1574
                size_t pl;
11✔
1575

1576
                pl = strlen(path);
11✔
1577
                assert(pl <= BUS_PATH_SIZE_MAX);
11✔
1578
                prefix = new(char, pl + 1);
11✔
1579
                if (!prefix)
11✔
1580
                        return -ENOMEM;
×
1581

1582
                OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
22✔
1583
                        n = hashmap_get(bus->nodes, prefix);
11✔
1584
                        if (n)
11✔
1585
                                break;
1586
                }
1587
        }
1588

1589
        while (n && !n->object_managers)
12✔
1590
                n = n->parent;
×
1591

1592
        if (out)
12✔
1593
                *out = n;
12✔
1594
        return !!n;
12✔
1595
}
1596

1597
static int bus_add_object(
×
1598
                sd_bus *bus,
1599
                sd_bus_slot **slot,
1600
                bool fallback,
1601
                const char *path,
1602
                sd_bus_message_handler_t callback,
1603
                void *userdata) {
1604

1605
        sd_bus_slot *s;
×
1606
        struct node *n;
×
1607
        int r;
×
1608

1609
        assert_return(bus, -EINVAL);
×
1610
        assert_return(bus = bus_resolve(bus), -ENOPKG);
×
1611
        assert_return(object_path_is_valid(path), -EINVAL);
×
1612
        assert_return(callback, -EINVAL);
×
1613
        assert_return(!bus_origin_changed(bus), -ECHILD);
×
1614

1615
        n = bus_node_allocate(bus, path);
×
1616
        if (!n)
×
1617
                return -ENOMEM;
1618

1619
        s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
×
1620
        if (!s) {
×
1621
                r = -ENOMEM;
×
1622
                goto fail;
×
1623
        }
1624

1625
        s->node_callback.callback = callback;
×
1626
        s->node_callback.is_fallback = fallback;
×
1627

1628
        s->node_callback.node = n;
×
1629
        LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
×
1630
        bus->nodes_modified = true;
×
1631

1632
        if (slot)
×
1633
                *slot = s;
×
1634

1635
        return 0;
1636

1637
fail:
×
1638
        sd_bus_slot_unref(s);
×
1639
        bus_node_gc(bus, n);
×
1640

1641
        return r;
×
1642
}
1643

1644
_public_ int sd_bus_add_object(
×
1645
                sd_bus *bus,
1646
                sd_bus_slot **slot,
1647
                const char *path,
1648
                sd_bus_message_handler_t callback,
1649
                void *userdata) {
1650

1651
        return bus_add_object(bus, slot, false, path, callback, userdata);
×
1652
}
1653

1654
_public_ int sd_bus_add_fallback(
×
1655
                sd_bus *bus,
1656
                sd_bus_slot **slot,
1657
                const char *prefix,
1658
                sd_bus_message_handler_t callback,
1659
                void *userdata) {
1660

1661
        return bus_add_object(bus, slot, true, prefix, callback, userdata);
×
1662
}
1663

1664
static void vtable_member_hash_func(const struct vtable_member *m, struct siphash *state) {
6,942,563✔
1665
        assert(m);
6,942,563✔
1666

1667
        string_hash_func(m->path, state);
6,942,563✔
1668
        string_hash_func(m->interface, state);
6,942,563✔
1669
        string_hash_func(m->member, state);
6,942,563✔
1670
}
6,942,563✔
1671

1672
static int vtable_member_compare_func(const struct vtable_member *x, const struct vtable_member *y) {
3,396,029✔
1673
        int r;
3,396,029✔
1674

1675
        assert(x);
3,396,029✔
1676
        assert(y);
3,396,029✔
1677

1678
        r = strcmp(x->path, y->path);
3,396,029✔
1679
        if (r != 0)
3,396,029✔
1680
                return r;
1681

1682
        r = strcmp(x->interface, y->interface);
3,087,039✔
1683
        if (r != 0)
3,087,039✔
1684
                return r;
1685

1686
        return strcmp(x->member, y->member);
2,323,634✔
1687
}
1688

1689
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1690
                vtable_member_hash_ops,
1691
                struct vtable_member, vtable_member_hash_func, vtable_member_compare_func, free);
1692

1693
typedef enum {
1694
        NAMES_FIRST_PART        = 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */
1695
        NAMES_PRESENT           = 1 << 1, /* at least one argument name is present, so the names will checked.
1696
                                             This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list  */
1697
        NAMES_SINGLE_PART       = 1 << 2, /* argument name list consisting of a single part */
1698
} names_flags;
1699

1700
static bool names_are_valid(const char *signature, const char **names, names_flags *flags) {
342,285✔
1701
        int r;
342,285✔
1702

1703
        if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0')
342,285✔
1704
                *flags |= NAMES_PRESENT;
150,431✔
1705

1706
        while (*flags & NAMES_PRESENT) {
704,326✔
1707
                size_t l;
655,855✔
1708

1709
                if (!*signature)
655,855✔
1710
                        break;
1711

1712
                r = signature_element_length(signature, &l);
362,041✔
1713
                if (r < 0)
362,041✔
1714
                        return false;
×
1715

1716
                if (**names != '\0') {
362,041✔
1717
                        if (!member_name_is_valid(*names))
362,041✔
1718
                                return false;
1719
                        *names += strlen(*names) + 1;
362,041✔
1720
                } else if (*flags & NAMES_PRESENT)
×
1721
                        return false;
1722

1723
                signature += l;
362,041✔
1724
        }
1725
        /* let's check if there are more argument names specified than the signature allows */
1726
        if (*flags & NAMES_PRESENT && **names != '\0' && !(*flags & NAMES_FIRST_PART))
342,285✔
1727
                return false;
1728
        *flags &= ~NAMES_FIRST_PART;
342,285✔
1729
        return true;
342,285✔
1730
}
1731

1732
/* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions
1733
   to make sure the calling code is compatible with one of these */
1734
struct sd_bus_vtable_221 {
1735
        uint8_t type:8;
1736
        uint64_t flags:56;
1737
        union {
1738
                struct {
1739
                        size_t element_size;
1740
                } start;
1741
                struct {
1742
                        const char *member;
1743
                        const char *signature;
1744
                        const char *result;
1745
                        sd_bus_message_handler_t handler;
1746
                        size_t offset;
1747
                } method;
1748
                struct {
1749
                        const char *member;
1750
                        const char *signature;
1751
                } signal;
1752
                struct {
1753
                        const char *member;
1754
                        const char *signature;
1755
                        sd_bus_property_get_t get;
1756
                        sd_bus_property_set_t set;
1757
                        size_t offset;
1758
                } property;
1759
        } x;
1760
};
1761
/* Structure size up to v241 */
1762
#define VTABLE_ELEMENT_SIZE_221 sizeof(struct sd_bus_vtable_221)
1763

1764
/* Size of the structure when "features" field was added. If the structure definition is augmented, a copy of
1765
 * the structure definition will need to be made (similarly to the sd_bus_vtable_221 above), and this
1766
 * definition updated to refer to it. */
1767
#define VTABLE_ELEMENT_SIZE_242 sizeof(struct sd_bus_vtable)
1768

1769
static int vtable_features(const sd_bus_vtable *vtable) {
183,739✔
1770
        if (vtable[0].x.start.element_size < VTABLE_ELEMENT_SIZE_242 ||
183,739✔
1771
            !vtable[0].x.start.vtable_format_reference)
183,734✔
1772
                return 0;
1773
        return vtable[0].x.start.features;
183,734✔
1774
}
1775

1776
bool bus_vtable_has_names(const sd_bus_vtable *vtable) {
183,739✔
1777
        return vtable_features(vtable) & _SD_BUS_VTABLE_PARAM_NAMES;
183,739✔
1778
}
1779

1780
const sd_bus_vtable* bus_vtable_next(const sd_bus_vtable *vtable, const sd_bus_vtable *v) {
26,664,740✔
1781
        return (const sd_bus_vtable*) ((char*) v + vtable[0].x.start.element_size);
26,664,740✔
1782
}
1783

1784
static int add_object_vtable_internal(
40,199✔
1785
                sd_bus *bus,
1786
                sd_bus_slot **slot,
1787
                const char *path,
1788
                const char *interface,
1789
                const sd_bus_vtable *vtable,
1790
                bool fallback,
1791
                sd_bus_object_find_t find,
1792
                void *userdata) {
1793

1794
        sd_bus_slot *s = NULL;
40,199✔
1795
        struct node_vtable *existing = NULL;
40,199✔
1796
        const sd_bus_vtable *v;
40,199✔
1797
        struct node *n;
40,199✔
1798
        int r;
40,199✔
1799
        const char *names = "";
40,199✔
1800
        names_flags nf;
40,199✔
1801

1802
        assert_return(bus, -EINVAL);
40,199✔
1803
        assert_return(bus = bus_resolve(bus), -ENOPKG);
40,199✔
1804
        assert_return(object_path_is_valid(path), -EINVAL);
40,199✔
1805
        assert_return(interface_name_is_valid(interface), -EINVAL);
40,199✔
1806
        assert_return(vtable, -EINVAL);
40,199✔
1807
        assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
40,199✔
1808
        assert_return(vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_221 ||
40,199✔
1809
                      vtable[0].x.start.element_size >= VTABLE_ELEMENT_SIZE_242,
1810
                      -EINVAL);
1811
        assert_return(!bus_origin_changed(bus), -ECHILD);
40,199✔
1812
        assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
40,199✔
1813
                      !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1814
                      !streq(interface, "org.freedesktop.DBus.Peer") &&
1815
                      !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1816

1817
        n = bus_node_allocate(bus, path);
40,199✔
1818
        if (!n)
40,199✔
1819
                return -ENOMEM;
1820

1821
        LIST_FOREACH(vtables, i, n->vtables) {
545,334✔
1822
                if (i->is_fallback != fallback) {
505,135✔
1823
                        r = -EPROTOTYPE;
×
1824
                        goto fail;
×
1825
                }
1826

1827
                if (streq(i->interface, interface)) {
505,135✔
1828

1829
                        if (i->vtable == vtable) {
46,648✔
1830
                                r = -EEXIST;
×
1831
                                goto fail;
×
1832
                        }
1833

1834
                        existing = i;
1835
                }
1836
        }
1837

1838
        s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
40,199✔
1839
        if (!s) {
40,199✔
1840
                r = -ENOMEM;
×
1841
                goto fail;
×
1842
        }
1843

1844
        s->node_vtable.is_fallback = fallback;
40,199✔
1845
        s->node_vtable.vtable = vtable;
40,199✔
1846
        s->node_vtable.find = find;
40,199✔
1847

1848
        s->node_vtable.interface = strdup(interface);
40,199✔
1849
        if (!s->node_vtable.interface) {
40,199✔
1850
                r = -ENOMEM;
×
1851
                goto fail;
×
1852
        }
1853

1854
        v = s->node_vtable.vtable;
40,199✔
1855
        for (v = bus_vtable_next(vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
1,951,275✔
1856

1857
                switch (v->type) {
1,911,076✔
1858

1859
                case _SD_BUS_VTABLE_METHOD: {
166,445✔
1860
                        struct vtable_member *m;
166,445✔
1861
                        nf = NAMES_FIRST_PART;
166,445✔
1862

1863
                        if (bus_vtable_has_names(vtable))
166,445✔
1864
                                names = strempty(v->x.method.names);
166,443✔
1865

1866
                        if (!member_name_is_valid(v->x.method.member) ||
332,890✔
1867
                            !signature_is_valid(strempty(v->x.method.signature), false) ||
375,666✔
1868
                            !signature_is_valid(strempty(v->x.method.result), false) ||
419,221✔
1869
                            !names_are_valid(strempty(v->x.method.signature), &names, &nf) ||
375,666✔
1870
                            !names_are_valid(strempty(v->x.method.result), &names, &nf) ||
252,776✔
1871
                            !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
166,445✔
1872
                            v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
166,445✔
1873
                                r = -EINVAL;
×
1874
                                goto fail;
×
1875
                        }
1876

1877
                        m = new0(struct vtable_member, 1);
166,445✔
1878
                        if (!m) {
166,445✔
1879
                                r = -ENOMEM;
×
1880
                                goto fail;
×
1881
                        }
1882

1883
                        m->parent = &s->node_vtable;
166,445✔
1884
                        m->path = n->path;
166,445✔
1885
                        m->interface = s->node_vtable.interface;
166,445✔
1886
                        m->member = v->x.method.member;
166,445✔
1887
                        m->vtable = v;
166,445✔
1888

1889
                        r = set_ensure_put(&bus->vtable_methods, &vtable_member_hash_ops, m);
166,445✔
1890
                        if (r == 0)
166,445✔
1891
                                r = -EEXIST;
1892
                        if (r < 0) {
166,445✔
1893
                                free(m);
×
1894
                                goto fail;
×
1895
                        }
1896

1897
                        break;
1898
                }
1899

1900
                case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
13,444✔
1901

1902
                        if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
13,444✔
1903
                                r = -EINVAL;
×
1904
                                goto fail;
×
1905
                        }
1906

1907
                        if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
13,444✔
1908
                                r = -EINVAL;
×
1909
                                goto fail;
×
1910
                        }
1911

1912
                        _fallthrough_;
1,735,236✔
1913
                case _SD_BUS_VTABLE_PROPERTY: {
1914
                        struct vtable_member *m;
1,735,236✔
1915

1916
                        if (!member_name_is_valid(v->x.property.member) ||
3,470,472✔
1917
                            !signature_is_single(v->x.property.signature, false) ||
1,735,236✔
1918
                            !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1,735,236✔
1919
                            (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1,735,236✔
1920
                            (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1,735,236✔
1921
                            ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1,735,236✔
1922
                            (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
×
1923
                                r = -EINVAL;
×
1924
                                goto fail;
×
1925
                        }
1926

1927
                        m = new0(struct vtable_member, 1);
1,735,236✔
1928
                        if (!m) {
1,735,236✔
1929
                                r = -ENOMEM;
×
1930
                                goto fail;
×
1931
                        }
1932

1933
                        m->parent = &s->node_vtable;
1,735,236✔
1934
                        m->path = n->path;
1,735,236✔
1935
                        m->interface = s->node_vtable.interface;
1,735,236✔
1936
                        m->member = v->x.property.member;
1,735,236✔
1937
                        m->vtable = v;
1,735,236✔
1938

1939
                        r = set_ensure_put(&bus->vtable_properties, &vtable_member_hash_ops, m);
1,735,236✔
1940
                        if (r == 0)
1,735,236✔
1941
                                r = -EEXIST;
1942
                        if (r < 0) {
1,735,236✔
1943
                                free(m);
×
1944
                                goto fail;
×
1945
                        }
1946

1947
                        break;
1948
                }
1949

1950
                case _SD_BUS_VTABLE_SIGNAL:
9,395✔
1951
                        nf = NAMES_SINGLE_PART;
9,395✔
1952

1953
                        if (bus_vtable_has_names(vtable))
9,395✔
1954
                                names = strempty(v->x.signal.names);
9,395✔
1955

1956
                        if (!member_name_is_valid(v->x.signal.member) ||
18,790✔
1957
                            !signature_is_valid(strempty(v->x.signal.signature), false) ||
21,125✔
1958
                            !names_are_valid(strempty(v->x.signal.signature), &names, &nf) ||
11,730✔
1959
                            v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
9,395✔
1960
                                r = -EINVAL;
×
1961
                                goto fail;
×
1962
                        }
1963

1964
                        break;
1965

1966
                default:
×
1967
                        r = -EINVAL;
×
1968
                        goto fail;
×
1969
                }
1970
        }
1971

1972
        s->node_vtable.node = n;
40,199✔
1973
        LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
40,199✔
1974
        bus->nodes_modified = true;
40,199✔
1975

1976
        if (slot)
40,199✔
1977
                *slot = s;
×
1978

1979
        return 0;
1980

1981
fail:
×
1982
        sd_bus_slot_unref(s);
×
1983
        bus_node_gc(bus, n);
×
1984

1985
        return r;
1986
}
1987

1988
/* This symbol exists solely to tell the linker that the "new" vtable format is used. */
1989
_public_ const unsigned sd_bus_object_vtable_format = 242;
1990

1991
_public_ int sd_bus_add_object_vtable(
4,836✔
1992
                sd_bus *bus,
1993
                sd_bus_slot **slot,
1994
                const char *path,
1995
                const char *interface,
1996
                const sd_bus_vtable *vtable,
1997
                void *userdata) {
1998

1999
        return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
4,836✔
2000
}
2001

2002
_public_ int sd_bus_add_fallback_vtable(
35,363✔
2003
                sd_bus *bus,
2004
                sd_bus_slot **slot,
2005
                const char *prefix,
2006
                const char *interface,
2007
                const sd_bus_vtable *vtable,
2008
                sd_bus_object_find_t find,
2009
                void *userdata) {
2010

2011
        return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
35,363✔
2012
}
2013

2014
_public_ int sd_bus_add_node_enumerator(
4,899✔
2015
                sd_bus *bus,
2016
                sd_bus_slot **slot,
2017
                const char *path,
2018
                sd_bus_node_enumerator_t callback,
2019
                void *userdata) {
2020

2021
        sd_bus_slot *s;
4,899✔
2022
        struct node *n;
4,899✔
2023
        int r;
4,899✔
2024

2025
        assert_return(bus, -EINVAL);
4,899✔
2026
        assert_return(bus = bus_resolve(bus), -ENOPKG);
4,899✔
2027
        assert_return(object_path_is_valid(path), -EINVAL);
4,899✔
2028
        assert_return(callback, -EINVAL);
4,899✔
2029
        assert_return(!bus_origin_changed(bus), -ECHILD);
4,899✔
2030

2031
        n = bus_node_allocate(bus, path);
4,899✔
2032
        if (!n)
4,899✔
2033
                return -ENOMEM;
2034

2035
        s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
4,899✔
2036
        if (!s) {
4,899✔
2037
                r = -ENOMEM;
×
2038
                goto fail;
×
2039
        }
2040

2041
        s->node_enumerator.callback = callback;
4,899✔
2042

2043
        s->node_enumerator.node = n;
4,899✔
2044
        LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
4,899✔
2045
        bus->nodes_modified = true;
4,899✔
2046

2047
        if (slot)
4,899✔
2048
                *slot = s;
×
2049

2050
        return 0;
2051

2052
fail:
×
2053
        sd_bus_slot_unref(s);
×
2054
        bus_node_gc(bus, n);
×
2055

2056
        return r;
×
2057
}
2058

2059
static int emit_properties_changed_on_interface(
254,474✔
2060
                sd_bus *bus,
2061
                const char *prefix,
2062
                const char *path,
2063
                const char *interface,
2064
                bool require_fallback,
2065
                bool *found_interface,
2066
                char **names) {
2067

2068
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
2069
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
254,474✔
2070
        bool has_invalidating = false, has_changing = false;
254,474✔
2071
        struct vtable_member key = {};
254,474✔
2072
        struct node *n;
254,474✔
2073
        void *u = NULL;
254,474✔
2074
        int r;
254,474✔
2075

2076
        assert(bus);
254,474✔
2077
        assert(prefix);
254,474✔
2078
        assert(path);
254,474✔
2079
        assert(interface);
254,474✔
2080
        assert(found_interface);
254,474✔
2081

2082
        n = hashmap_get(bus->nodes, prefix);
254,474✔
2083
        if (!n)
254,474✔
2084
                return 0;
2085

2086
        r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
167,683✔
2087
        if (r < 0)
167,683✔
2088
                return r;
2089

2090
        r = sd_bus_message_append(m, "s", interface);
167,683✔
2091
        if (r < 0)
167,683✔
2092
                return r;
2093

2094
        r = sd_bus_message_open_container(m, 'a', "{sv}");
167,683✔
2095
        if (r < 0)
167,683✔
2096
                return r;
2097

2098
        key.path = prefix;
167,683✔
2099
        key.interface = interface;
167,683✔
2100

2101
        LIST_FOREACH(vtables, c, n->vtables) {
2,718,967✔
2102
                if (require_fallback && !c->is_fallback)
2,551,284✔
2103
                        continue;
12,658✔
2104

2105
                if (!streq(c->interface, interface))
2,538,626✔
2106
                        continue;
2,349,330✔
2107

2108
                r = node_vtable_get_userdata(bus, path, c, &u, &error);
189,296✔
2109
                if (r < 0)
189,296✔
2110
                        return r;
2111
                if (bus->nodes_modified)
189,296✔
2112
                        return 0;
2113
                if (r == 0)
189,296✔
2114
                        continue;
1✔
2115

2116
                *found_interface = true;
189,295✔
2117

2118
                if (names) {
189,295✔
2119
                        /* If the caller specified a list of
2120
                         * properties we include exactly those in the
2121
                         * PropertiesChanged message */
2122

2123
                        STRV_FOREACH(property, names) {
94,513✔
2124
                                struct vtable_member *v;
52,321✔
2125

2126
                                assert_return(member_name_is_valid(*property), -EINVAL);
52,321✔
2127

2128
                                key.member = *property;
52,321✔
2129
                                v = set_get(bus->vtable_properties, &key);
52,321✔
2130
                                if (!v)
52,321✔
2131
                                        return -ENOENT;
2132

2133
                                /* If there are two vtables for the same
2134
                                 * interface, let's handle this property when
2135
                                 * we come to that vtable. */
2136
                                if (c != v->parent)
52,321✔
2137
                                        continue;
×
2138

2139
                                assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
52,321✔
2140
                                              v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
2141

2142
                                assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
52,321✔
2143

2144
                                if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
52,321✔
2145
                                        has_invalidating = true;
×
2146
                                        continue;
×
2147
                                }
2148

2149
                                has_changing = true;
52,321✔
2150

2151
                                r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
52,321✔
2152
                                if (r < 0)
52,321✔
2153
                                        return r;
2154
                                if (bus->nodes_modified)
52,321✔
2155
                                        return 0;
2156
                        }
2157
                } else {
2158
                        const sd_bus_vtable *v;
147,103✔
2159

2160
                        /* If the caller specified no properties list
2161
                         * we include all properties that are marked
2162
                         * as changing in the message. */
2163

2164
                        v = c->vtable;
147,103✔
2165
                        for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
11,190,514✔
2166
                                if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
11,043,411✔
2167
                                        continue;
733,987✔
2168

2169
                                if (v->flags & SD_BUS_VTABLE_HIDDEN)
10,309,424✔
2170
                                        continue;
404,204✔
2171

2172
                                if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
9,905,220✔
2173
                                        has_invalidating = true;
237,322✔
2174
                                        continue;
237,322✔
2175
                                }
2176

2177
                                if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
9,667,898✔
2178
                                        continue;
8,584,207✔
2179

2180
                                has_changing = true;
1,083,691✔
2181

2182
                                r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1,083,691✔
2183
                                if (r < 0)
1,083,691✔
2184
                                        return r;
2185
                                if (bus->nodes_modified)
1,083,691✔
2186
                                        return 0;
2187
                        }
2188
                }
2189
        }
2190

2191
        if (!has_invalidating && !has_changing)
167,683✔
2192
                return 0;
2193

2194
        r = sd_bus_message_close_container(m);
104,391✔
2195
        if (r < 0)
104,391✔
2196
                return r;
2197

2198
        r = sd_bus_message_open_container(m, 'a', "s");
104,391✔
2199
        if (r < 0)
104,391✔
2200
                return r;
2201

2202
        if (has_invalidating) {
104,391✔
2203
                LIST_FOREACH(vtables, c, n->vtables) {
1,878,096✔
2204
                        if (require_fallback && !c->is_fallback)
1,822,840✔
2205
                                continue;
×
2206

2207
                        if (!streq(c->interface, interface))
1,822,840✔
2208
                                continue;
1,698,576✔
2209

2210
                        r = node_vtable_get_userdata(bus, path, c, &u, &error);
124,264✔
2211
                        if (r < 0)
124,264✔
2212
                                return r;
2213
                        if (bus->nodes_modified)
124,264✔
2214
                                return 0;
2215
                        if (r == 0)
124,264✔
2216
                                continue;
×
2217

2218
                        if (names) {
124,264✔
2219
                                STRV_FOREACH(property, names) {
×
2220
                                        struct vtable_member *v;
×
2221

2222
                                        key.member = *property;
×
2223
                                        assert_se(v = set_get(bus->vtable_properties, &key));
×
2224
                                        assert(c == v->parent);
×
2225

2226
                                        if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
×
2227
                                                continue;
×
2228

2229
                                        r = sd_bus_message_append(m, "s", *property);
×
2230
                                        if (r < 0)
×
2231
                                                return r;
2232
                                }
2233
                        } else {
2234
                                const sd_bus_vtable *v;
124,264✔
2235

2236
                                v = c->vtable;
124,264✔
2237
                                for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
10,973,420✔
2238
                                        if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
10,849,156✔
2239
                                                continue;
714,379✔
2240

2241
                                        if (v->flags & SD_BUS_VTABLE_HIDDEN)
10,134,777✔
2242
                                                continue;
403,754✔
2243

2244
                                        if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
9,731,023✔
2245
                                                continue;
9,493,701✔
2246

2247
                                        r = sd_bus_message_append(m, "s", v->x.property.member);
237,322✔
2248
                                        if (r < 0)
237,322✔
2249
                                                return r;
2250
                                }
2251
                        }
2252
                }
2253
        }
2254

2255
        r = sd_bus_message_close_container(m);
104,391✔
2256
        if (r < 0)
104,391✔
2257
                return r;
2258

2259
        r = sd_bus_send(bus, m, NULL);
104,391✔
2260
        if (r < 0)
104,391✔
2261
                return r;
127✔
2262

2263
        return 1;
2264
}
2265

2266
_public_ int sd_bus_emit_properties_changed_strv(
117,065✔
2267
                sd_bus *bus,
2268
                const char *path,
2269
                const char *interface,
2270
                char **names) {
2271

2272
        _cleanup_free_ char *prefix = NULL;
117,065✔
2273
        bool found_interface = false;
117,065✔
2274
        size_t pl;
117,065✔
2275
        int r;
117,065✔
2276

2277
        assert_return(bus, -EINVAL);
117,065✔
2278
        assert_return(bus = bus_resolve(bus), -ENOPKG);
117,065✔
2279
        assert_return(object_path_is_valid(path), -EINVAL);
117,065✔
2280
        assert_return(interface_name_is_valid(interface), -EINVAL);
117,065✔
2281
        assert_return(!bus_origin_changed(bus), -ECHILD);
117,065✔
2282

2283
        if (!BUS_IS_OPEN(bus->state))
117,065✔
2284
                return -ENOTCONN;
2285

2286
        /* A non-NULL but empty names list means nothing needs to be
2287
           generated. A NULL list OTOH indicates that all properties
2288
           that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2289
           included in the PropertiesChanged message. */
2290
        if (names && names[0] == NULL)
117,049✔
2291
                return 0;
2292

2293
        BUS_DONT_DESTROY(bus);
234,114✔
2294

2295
        pl = strlen(path);
117,049✔
2296
        assert(pl <= BUS_PATH_SIZE_MAX);
117,049✔
2297
        prefix = new(char, pl + 1);
117,049✔
2298
        if (!prefix)
117,049✔
2299
                return -ENOMEM;
2300

2301
        do {
117,049✔
2302
                bus->nodes_modified = false;
117,049✔
2303

2304
                r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
117,049✔
2305
                if (r != 0)
117,049✔
2306
                        return r;
2307
                if (bus->nodes_modified)
86,793✔
2308
                        continue;
×
2309

2310
                OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
300,166✔
2311
                        r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
137,425✔
2312
                        if (r != 0)
137,425✔
2313
                                return r;
2314
                        if (bus->nodes_modified)
63,290✔
2315
                                break;
2316
                }
2317

2318
        } while (bus->nodes_modified);
12,658✔
2319

2320
        return found_interface ? 0 : -ENOENT;
12,658✔
2321
}
2322

2323
_public_ int sd_bus_emit_properties_changed(
5,490✔
2324
                sd_bus *bus,
2325
                const char *path,
2326
                const char *interface,
2327
                const char *name, ...)  {
2328

2329
        char **names;
5,490✔
2330

2331
        assert_return(bus, -EINVAL);
5,490✔
2332
        assert_return(bus = bus_resolve(bus), -ENOPKG);
5,490✔
2333
        assert_return(object_path_is_valid(path), -EINVAL);
5,490✔
2334
        assert_return(interface_name_is_valid(interface), -EINVAL);
5,490✔
2335
        assert_return(!bus_origin_changed(bus), -ECHILD);
5,490✔
2336

2337
        if (!BUS_IS_OPEN(bus->state))
5,490✔
2338
                return -ENOTCONN;
2339

2340
        if (!name)
5,490✔
2341
                return 0;
2342

2343
        names = strv_from_stdarg_alloca(name);
6,622✔
2344

2345
        return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
5,490✔
2346
}
2347

2348
static int object_added_append_all_prefix(
31✔
2349
                sd_bus *bus,
2350
                sd_bus_message *m,
2351
                OrderedSet *s,
2352
                const char *prefix,
2353
                const char *path,
2354
                bool require_fallback) {
2355

2356
        const char *previous_interface = NULL;
31✔
2357
        struct node *n;
31✔
2358
        int r;
31✔
2359

2360
        assert(bus);
31✔
2361
        assert(m);
31✔
2362
        assert(s);
31✔
2363
        assert(prefix);
31✔
2364
        assert(path);
31✔
2365

2366
        n = hashmap_get(bus->nodes, prefix);
31✔
2367
        if (!n)
31✔
2368
                return 0;
2369

2370
        LIST_FOREACH(vtables, c, n->vtables) {
36✔
2371
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
10✔
2372
                void *u = NULL;
10✔
2373

2374
                if (require_fallback && !c->is_fallback)
10✔
2375
                        continue;
4✔
2376

2377
                r = node_vtable_get_userdata(bus, path, c, &u, &error);
6✔
2378
                if (r < 0)
6✔
2379
                        return r;
2380
                if (bus->nodes_modified)
6✔
2381
                        return 0;
2382
                if (r == 0)
6✔
2383
                        continue;
×
2384

2385
                if (!streq_ptr(c->interface, previous_interface)) {
6✔
2386
                        /* If a child-node already handled this interface, we
2387
                         * skip it on any of its parents. The child vtables
2388
                         * always fully override any conflicting vtables of
2389
                         * any parent node. */
2390
                        if (ordered_set_get(s, c->interface))
6✔
2391
                                continue;
×
2392

2393
                        r = ordered_set_put(s, c->interface);
6✔
2394
                        if (r < 0)
6✔
2395
                                return r;
2396

2397
                        if (previous_interface) {
6✔
2398
                                r = sd_bus_message_close_container(m);
×
2399
                                if (r < 0)
×
2400
                                        return r;
2401
                                r = sd_bus_message_close_container(m);
×
2402
                                if (r < 0)
×
2403
                                        return r;
2404
                        }
2405

2406
                        r = sd_bus_message_open_container(m, 'e', "sa{sv}");
6✔
2407
                        if (r < 0)
6✔
2408
                                return r;
2409
                        r = sd_bus_message_append(m, "s", c->interface);
6✔
2410
                        if (r < 0)
6✔
2411
                                return r;
2412
                        r = sd_bus_message_open_container(m, 'a', "{sv}");
6✔
2413
                        if (r < 0)
6✔
2414
                                return r;
2415

2416
                        previous_interface = c->interface;
6✔
2417
                }
2418

2419
                r = vtable_append_all_properties(bus, m, path, c, u, &error);
6✔
2420
                if (r < 0)
6✔
2421
                        return r;
2422
                if (bus->nodes_modified)
6✔
2423
                        return 0;
2424
        }
2425

2426
        if (previous_interface) {
26✔
2427
                r = sd_bus_message_close_container(m);
6✔
2428
                if (r < 0)
6✔
2429
                        return r;
2430
                r = sd_bus_message_close_container(m);
6✔
2431
                if (r < 0)
6✔
2432
                        return r;
×
2433
        }
2434

2435
        return 0;
2436
}
2437

2438
static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) {
6✔
2439
        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
×
2440
        _cleanup_free_ char *prefix = NULL;
6✔
2441
        size_t pl;
6✔
2442
        int r;
6✔
2443

2444
        assert(bus);
6✔
2445
        assert(m);
6✔
2446
        assert(path);
6✔
2447

2448
        /*
2449
         * This appends all interfaces registered on path @path. We first add
2450
         * the builtin interfaces, which are always available and handled by
2451
         * sd-bus. Then, we add all interfaces registered on the exact node,
2452
         * followed by all fallback interfaces registered on any parent prefix.
2453
         *
2454
         * If an interface is registered multiple times on the same node with
2455
         * different vtables, we merge all the properties across all vtables.
2456
         * However, if a child node has the same interface registered as one of
2457
         * its parent nodes has as fallback, we make the child overwrite the
2458
         * parent instead of extending it. Therefore, we keep a "Set" of all
2459
         * handled interfaces during parent traversal, so we skip interfaces on
2460
         * a parent that were overwritten by a child.
2461
         */
2462

2463
        s = ordered_set_new(&string_hash_ops);
6✔
2464
        if (!s)
6✔
2465
                return -ENOMEM;
2466

2467
        r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
6✔
2468
        if (r < 0)
6✔
2469
                return r;
2470
        r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
6✔
2471
        if (r < 0)
6✔
2472
                return r;
2473
        r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
6✔
2474
        if (r < 0)
6✔
2475
                return r;
2476
        if (path_has_object_manager){
6✔
2477
                r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1✔
2478
                if (r < 0)
1✔
2479
                        return r;
2480
        }
2481

2482
        r = object_added_append_all_prefix(bus, m, s, path, path, false);
6✔
2483
        if (r < 0)
6✔
2484
                return r;
2485
        if (bus->nodes_modified)
6✔
2486
                return 0;
2487

2488
        pl = strlen(path);
6✔
2489
        assert(pl <= BUS_PATH_SIZE_MAX);
6✔
2490
        prefix = new(char, pl + 1);
6✔
2491
        if (!prefix)
6✔
2492
                return -ENOMEM;
2493

2494
        OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
62✔
2495
                r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
25✔
2496
                if (r < 0)
25✔
2497
                        return r;
2498
                if (bus->nodes_modified)
25✔
2499
                        return 0;
2500
        }
2501

2502
        return 0;
2503
}
2504

2505
_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
6✔
2506
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
6✔
2507
        struct node *object_manager;
6✔
2508
        int r;
6✔
2509

2510
        /*
2511
         * This emits an InterfacesAdded signal on the given path, by iterating
2512
         * all registered vtables and fallback vtables on the path. All
2513
         * properties are queried and included in the signal.
2514
         * This call is equivalent to sd_bus_emit_interfaces_added() with an
2515
         * explicit list of registered interfaces. However, unlike
2516
         * interfaces_added(), this call can figure out the list of supported
2517
         * interfaces itself. Furthermore, it properly adds the builtin
2518
         * org.freedesktop.DBus.* interfaces.
2519
         */
2520

2521
        assert_return(bus, -EINVAL);
6✔
2522
        assert_return(bus = bus_resolve(bus), -ENOPKG);
6✔
2523
        assert_return(object_path_is_valid(path), -EINVAL);
6✔
2524
        assert_return(!bus_origin_changed(bus), -ECHILD);
6✔
2525

2526
        if (!BUS_IS_OPEN(bus->state))
6✔
2527
                return -ENOTCONN;
2528

2529
        bool path_has_object_manager = false;
6✔
2530
        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
6✔
2531
        if (r < 0)
6✔
2532
                return r;
2533
        if (r == 0)
6✔
2534
                return -ESRCH;
2535

2536
        BUS_DONT_DESTROY(bus);
12✔
2537

2538
        do {
6✔
2539
                bus->nodes_modified = false;
6✔
2540
                m = sd_bus_message_unref(m);
6✔
2541

2542
                r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
6✔
2543
                if (r < 0)
6✔
2544
                        return r;
2545

2546
                r = sd_bus_message_append_basic(m, 'o', path);
6✔
2547
                if (r < 0)
6✔
2548
                        return r;
2549

2550
                r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
6✔
2551
                if (r < 0)
6✔
2552
                        return r;
2553

2554
                r = object_added_append_all(bus, m, path, path_has_object_manager);
6✔
2555
                if (r < 0)
6✔
2556
                        return r;
2557

2558
                if (bus->nodes_modified)
6✔
2559
                        continue;
×
2560

2561
                r = sd_bus_message_close_container(m);
6✔
2562
                if (r < 0)
6✔
2563
                        return r;
2564

2565
        } while (bus->nodes_modified);
6✔
2566

2567
        return sd_bus_send(bus, m, NULL);
6✔
2568
}
2569

2570
static int object_removed_append_all_prefix(
22✔
2571
                sd_bus *bus,
2572
                sd_bus_message *m,
2573
                OrderedSet *s,
2574
                const char *prefix,
2575
                const char *path,
2576
                bool require_fallback) {
2577

2578
        const char *previous_interface = NULL;
22✔
2579
        struct node *n;
22✔
2580
        int r;
22✔
2581

2582
        assert(bus);
22✔
2583
        assert(m);
22✔
2584
        assert(s);
22✔
2585
        assert(prefix);
22✔
2586
        assert(path);
22✔
2587

2588
        n = hashmap_get(bus->nodes, prefix);
22✔
2589
        if (!n)
22✔
2590
                return 0;
2591

2592
        LIST_FOREACH(vtables, c, n->vtables) {
25✔
2593
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
2594
                void *u = NULL;
7✔
2595

2596
                if (require_fallback && !c->is_fallback)
7✔
2597
                        continue;
3✔
2598
                if (streq_ptr(c->interface, previous_interface))
4✔
2599
                        continue;
×
2600

2601
                /* If a child-node already handled this interface, we
2602
                 * skip it on any of its parents. The child vtables
2603
                 * always fully override any conflicting vtables of
2604
                 * any parent node. */
2605
                if (ordered_set_get(s, c->interface))
4✔
2606
                        continue;
×
2607

2608
                r = node_vtable_get_userdata(bus, path, c, &u, &error);
4✔
2609
                if (r < 0)
4✔
2610
                        return r;
2611
                if (bus->nodes_modified)
4✔
2612
                        return 0;
2613
                if (r == 0)
4✔
2614
                        continue;
×
2615

2616
                r = ordered_set_put(s, c->interface);
4✔
2617
                if (r < 0)
4✔
2618
                        return r;
2619

2620
                r = sd_bus_message_append(m, "s", c->interface);
4✔
2621
                if (r < 0)
4✔
2622
                        return r;
2623

2624
                previous_interface = c->interface;
4✔
2625
        }
2626

2627
        return 0;
2628
}
2629

2630
static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) {
4✔
2631
        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
×
2632
        _cleanup_free_ char *prefix = NULL;
4✔
2633
        size_t pl;
4✔
2634
        int r;
4✔
2635

2636
        assert(bus);
4✔
2637
        assert(m);
4✔
2638
        assert(path);
4✔
2639

2640
        /* see sd_bus_emit_object_added() for details */
2641

2642
        s = ordered_set_new(&string_hash_ops);
4✔
2643
        if (!s)
4✔
2644
                return -ENOMEM;
2645

2646
        r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
4✔
2647
        if (r < 0)
4✔
2648
                return r;
2649
        r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
4✔
2650
        if (r < 0)
4✔
2651
                return r;
2652
        r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
4✔
2653
        if (r < 0)
4✔
2654
                return r;
2655

2656
        if (path_has_object_manager){
4✔
2657
                r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
×
2658
                if (r < 0)
×
2659
                        return r;
2660
        }
2661

2662
        r = object_removed_append_all_prefix(bus, m, s, path, path, false);
4✔
2663
        if (r < 0)
4✔
2664
                return r;
2665
        if (bus->nodes_modified)
4✔
2666
                return 0;
2667

2668
        pl = strlen(path);
4✔
2669
        assert(pl <= BUS_PATH_SIZE_MAX);
4✔
2670
        prefix = new(char, pl + 1);
4✔
2671
        if (!prefix)
4✔
2672
                return -ENOMEM;
2673

2674
        OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
44✔
2675
                r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
18✔
2676
                if (r < 0)
18✔
2677
                        return r;
2678
                if (bus->nodes_modified)
18✔
2679
                        return 0;
2680
        }
2681

2682
        return 0;
2683
}
2684

2685
_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
4✔
2686
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
4✔
2687
        struct node *object_manager;
4✔
2688
        int r;
4✔
2689

2690
        /*
2691
         * This is like sd_bus_emit_object_added(), but emits an
2692
         * InterfacesRemoved signal on the given path. This only includes any
2693
         * registered interfaces but skips the properties. Note that this will
2694
         * call into the find() callbacks of any registered vtable. Therefore,
2695
         * you must call this function before destroying/unlinking your object.
2696
         * Otherwise, the list of interfaces will be incomplete. However, note
2697
         * that this will *NOT* call into any property callback. Therefore, the
2698
         * object might be in an "destructed" state, as long as we can find it.
2699
         */
2700

2701
        assert_return(bus, -EINVAL);
4✔
2702
        assert_return(bus = bus_resolve(bus), -ENOPKG);
4✔
2703
        assert_return(object_path_is_valid(path), -EINVAL);
4✔
2704
        assert_return(!bus_origin_changed(bus), -ECHILD);
4✔
2705

2706
        if (!BUS_IS_OPEN(bus->state))
4✔
2707
                return -ENOTCONN;
2708

2709
        bool path_has_object_manager = false;
4✔
2710
        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
4✔
2711
        if (r < 0)
4✔
2712
                return r;
2713
        if (r == 0)
4✔
2714
                return -ESRCH;
2715

2716
        BUS_DONT_DESTROY(bus);
8✔
2717

2718
        do {
4✔
2719
                bus->nodes_modified = false;
4✔
2720
                m = sd_bus_message_unref(m);
4✔
2721

2722
                r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
4✔
2723
                if (r < 0)
4✔
2724
                        return r;
2725

2726
                r = sd_bus_message_append_basic(m, 'o', path);
4✔
2727
                if (r < 0)
4✔
2728
                        return r;
2729

2730
                r = sd_bus_message_open_container(m, 'a', "s");
4✔
2731
                if (r < 0)
4✔
2732
                        return r;
2733

2734
                r = object_removed_append_all(bus, m, path, path_has_object_manager);
4✔
2735
                if (r < 0)
4✔
2736
                        return r;
2737

2738
                if (bus->nodes_modified)
4✔
2739
                        continue;
×
2740

2741
                r = sd_bus_message_close_container(m);
4✔
2742
                if (r < 0)
4✔
2743
                        return r;
2744

2745
        } while (bus->nodes_modified);
4✔
2746

2747
        return sd_bus_send(bus, m, NULL);
4✔
2748
}
2749

2750
static int interfaces_added_append_one_prefix(
3✔
2751
                sd_bus *bus,
2752
                sd_bus_message *m,
2753
                const char *prefix,
2754
                const char *path,
2755
                const char *interface,
2756
                bool require_fallback) {
2757

2758
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
2759
        bool found_interface = false;
3✔
2760
        struct node *n;
3✔
2761
        void *u = NULL;
3✔
2762
        int r;
3✔
2763

2764
        assert(bus);
3✔
2765
        assert(m);
3✔
2766
        assert(prefix);
3✔
2767
        assert(path);
3✔
2768
        assert(interface);
3✔
2769

2770
        n = hashmap_get(bus->nodes, prefix);
3✔
2771
        if (!n)
3✔
2772
                return 0;
2773

2774
        LIST_FOREACH(vtables, c, n->vtables) {
3✔
2775
                if (require_fallback && !c->is_fallback)
1✔
2776
                        continue;
×
2777

2778
                if (!streq(c->interface, interface))
1✔
2779
                        continue;
×
2780

2781
                r = node_vtable_get_userdata(bus, path, c, &u, &error);
1✔
2782
                if (r < 0)
1✔
2783
                        return r;
2784
                if (bus->nodes_modified)
1✔
2785
                        return 0;
2786
                if (r == 0)
1✔
2787
                        continue;
×
2788

2789
                if (!found_interface) {
1✔
2790
                        r = sd_bus_message_append_basic(m, 's', interface);
1✔
2791
                        if (r < 0)
1✔
2792
                                return r;
2793

2794
                        r = sd_bus_message_open_container(m, 'a', "{sv}");
1✔
2795
                        if (r < 0)
1✔
2796
                                return r;
2797

2798
                        found_interface = true;
2799
                }
2800

2801
                r = vtable_append_all_properties(bus, m, path, c, u, &error);
1✔
2802
                if (r < 0)
1✔
2803
                        return r;
2804
                if (bus->nodes_modified)
1✔
2805
                        return 0;
2806
        }
2807

2808
        if (found_interface) {
2✔
2809
                r = sd_bus_message_close_container(m);
1✔
2810
                if (r < 0)
1✔
2811
                        return r;
2812
        }
2813

2814
        return found_interface;
2✔
2815
}
2816

2817
static int interfaces_added_append_one(
1✔
2818
                sd_bus *bus,
2819
                sd_bus_message *m,
2820
                const char *path,
2821
                const char *interface) {
2822

2823
        _cleanup_free_ char *prefix = NULL;
1✔
2824
        size_t pl;
1✔
2825
        int r;
1✔
2826

2827
        assert(bus);
1✔
2828
        assert(m);
1✔
2829
        assert(path);
1✔
2830
        assert(interface);
1✔
2831

2832
        r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
1✔
2833
        if (r != 0)
1✔
2834
                return r;
2835
        if (bus->nodes_modified)
1✔
2836
                return 0;
2837

2838
        pl = strlen(path);
1✔
2839
        assert(pl <= BUS_PATH_SIZE_MAX);
1✔
2840
        prefix = new(char, pl + 1);
1✔
2841
        if (!prefix)
1✔
2842
                return -ENOMEM;
2843

2844
        OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
4✔
2845
                r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2✔
2846
                if (r != 0)
2✔
2847
                        return r;
2848
                if (bus->nodes_modified)
1✔
2849
                        return 0;
2850
        }
2851

2852
        return -ENOENT;
2853
}
2854

2855
_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
1✔
2856
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1✔
2857
        struct node *object_manager;
1✔
2858
        int r;
1✔
2859

2860
        assert_return(bus, -EINVAL);
1✔
2861
        assert_return(bus = bus_resolve(bus), -ENOPKG);
1✔
2862
        assert_return(object_path_is_valid(path), -EINVAL);
1✔
2863
        assert_return(!bus_origin_changed(bus), -ECHILD);
1✔
2864

2865
        if (!BUS_IS_OPEN(bus->state))
1✔
2866
                return -ENOTCONN;
2867

2868
        if (strv_isempty(interfaces))
1✔
2869
                return 0;
2870

2871
        bool path_has_object_manager = false;
1✔
2872
        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
1✔
2873
        if (r < 0)
1✔
2874
                return r;
2875
        if (r == 0)
1✔
2876
                return -ESRCH;
2877

2878
        BUS_DONT_DESTROY(bus);
2✔
2879

2880
        do {
1✔
2881
                bus->nodes_modified = false;
1✔
2882
                m = sd_bus_message_unref(m);
1✔
2883

2884
                r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
1✔
2885
                if (r < 0)
1✔
2886
                        return r;
2887

2888
                r = sd_bus_message_append_basic(m, 'o', path);
1✔
2889
                if (r < 0)
1✔
2890
                        return r;
2891

2892
                r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
1✔
2893
                if (r < 0)
1✔
2894
                        return r;
2895

2896
                STRV_FOREACH(i, interfaces) {
2✔
2897
                        assert_return(interface_name_is_valid(*i), -EINVAL);
1✔
2898

2899
                        r = sd_bus_message_open_container(m, 'e', "sa{sv}");
1✔
2900
                        if (r < 0)
1✔
2901
                                return r;
2902

2903
                        r = interfaces_added_append_one(bus, m, path, *i);
1✔
2904
                        if (r < 0)
1✔
2905
                                return r;
2906

2907
                        if (bus->nodes_modified)
1✔
2908
                                break;
2909

2910
                        r = sd_bus_message_close_container(m);
1✔
2911
                        if (r < 0)
1✔
2912
                                return r;
2913
                }
2914

2915
                if (bus->nodes_modified)
1✔
2916
                        continue;
×
2917

2918
                r = sd_bus_message_close_container(m);
1✔
2919
                if (r < 0)
1✔
2920
                        return r;
2921

2922
        } while (bus->nodes_modified);
1✔
2923

2924
        return sd_bus_send(bus, m, NULL);
1✔
2925
}
2926

2927
_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
1✔
2928
        char **interfaces;
1✔
2929

2930
        assert_return(bus, -EINVAL);
1✔
2931
        assert_return(bus = bus_resolve(bus), -ENOPKG);
1✔
2932
        assert_return(object_path_is_valid(path), -EINVAL);
1✔
2933
        assert_return(!bus_origin_changed(bus), -ECHILD);
1✔
2934

2935
        if (!BUS_IS_OPEN(bus->state))
1✔
2936
                return -ENOTCONN;
2937

2938
        interfaces = strv_from_stdarg_alloca(interface);
1✔
2939

2940
        return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
1✔
2941
}
2942

2943
_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
1✔
2944
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1✔
2945
        struct node *object_manager;
1✔
2946
        int r;
1✔
2947

2948
        assert_return(bus, -EINVAL);
1✔
2949
        assert_return(bus = bus_resolve(bus), -ENOPKG);
1✔
2950
        assert_return(object_path_is_valid(path), -EINVAL);
1✔
2951
        assert_return(!bus_origin_changed(bus), -ECHILD);
1✔
2952

2953
        if (!BUS_IS_OPEN(bus->state))
1✔
2954
                return -ENOTCONN;
2955

2956
        if (strv_isempty(interfaces))
2✔
2957
                return 0;
2958

2959
        bool path_has_object_manager = false;
1✔
2960
        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
1✔
2961
        if (r < 0)
1✔
2962
                return r;
2963
        if (r == 0)
1✔
2964
                return -ESRCH;
2965

2966
        r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
1✔
2967
        if (r < 0)
1✔
2968
                return r;
2969

2970
        r = sd_bus_message_append_basic(m, 'o', path);
1✔
2971
        if (r < 0)
1✔
2972
                return r;
2973

2974
        r = sd_bus_message_append_strv(m, interfaces);
1✔
2975
        if (r < 0)
1✔
2976
                return r;
2977

2978
        return sd_bus_send(bus, m, NULL);
1✔
2979
}
2980

2981
_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
1✔
2982
        char **interfaces;
1✔
2983

2984
        assert_return(bus, -EINVAL);
1✔
2985
        assert_return(bus = bus_resolve(bus), -ENOPKG);
1✔
2986
        assert_return(object_path_is_valid(path), -EINVAL);
1✔
2987
        assert_return(!bus_origin_changed(bus), -ECHILD);
1✔
2988

2989
        if (!BUS_IS_OPEN(bus->state))
1✔
2990
                return -ENOTCONN;
2991

2992
        interfaces = strv_from_stdarg_alloca(interface);
1✔
2993

2994
        return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
1✔
2995
}
2996

2997
_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
105✔
2998
        sd_bus_slot *s;
105✔
2999
        struct node *n;
105✔
3000
        int r;
105✔
3001

3002
        assert_return(bus, -EINVAL);
105✔
3003
        assert_return(bus = bus_resolve(bus), -ENOPKG);
105✔
3004
        assert_return(object_path_is_valid(path), -EINVAL);
105✔
3005
        assert_return(!bus_origin_changed(bus), -ECHILD);
105✔
3006

3007
        n = bus_node_allocate(bus, path);
105✔
3008
        if (!n)
105✔
3009
                return -ENOMEM;
3010

3011
        s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
105✔
3012
        if (!s) {
105✔
3013
                r = -ENOMEM;
×
3014
                goto fail;
×
3015
        }
3016

3017
        s->node_object_manager.node = n;
105✔
3018
        LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
105✔
3019
        bus->nodes_modified = true;
105✔
3020

3021
        if (slot)
105✔
3022
                *slot = s;
×
3023

3024
        return 0;
3025

3026
fail:
×
3027
        sd_bus_slot_unref(s);
×
3028
        bus_node_gc(bus, n);
×
3029

3030
        return r;
×
3031
}
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