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

eclipse-bluechi / bluechi / 13784373365

11 Mar 2025 09:15AM UTC coverage: 82.234% (-0.1%) from 82.337%
13784373365

push

github

web-flow
Fix debian packaging (#1059)

The recently introduced tmpfiles.d bluechi.conf has not yet
been added to the installation configuration for BlueChi's
debian package.

Signed-off-by: Michael Engel <mengel@redhat.com>

5610 of 6822 relevant lines covered (82.23%)

1180.35 hits per line

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

83.33
/src/controller/node.c
1
/*
2
 * Copyright Contributors to the Eclipse BlueChi project
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
#include <systemd/sd-bus.h>
7

8
#ifdef CONFIG_H_USE_SELINUX
9
#        include <selinux/selinux.h>
10
#endif
11

12
#include "libbluechi/bus/bus.h"
13
#include "libbluechi/bus/utils.h"
14
#include "libbluechi/common/parse-util.h"
15
#include "libbluechi/common/time-util.h"
16
#include "libbluechi/log/log.h"
17

18
#include "controller.h"
19
#include "job.h"
20
#include "metrics.h"
21
#include "monitor.h"
22
#include "node.h"
23
#include "proxy_monitor.h"
24

25
#define DEBUG_AGENT_MESSAGES 0
26

27
static void node_send_agent_subscribe_all(Node *node);
28
static void node_start_proxy_dependency_all(Node *node);
29
static int node_run_unit_lifecycle_method(sd_bus_message *m, Node *node, const char *job_type, const char *method);
30

31
static int node_method_register(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
32
static int node_disconnected(sd_bus_message *message, void *userdata, sd_bus_error *error);
33
static int node_method_list_units(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
34
static int node_method_list_unit_files(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
35
static int node_method_set_unit_properties(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
36
static int node_method_start_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
37
static int node_method_stop_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
38
static int node_method_restart_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
39
static int node_method_reload_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
40
static int node_method_passthrough_to_agent(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
41
static int node_method_set_log_level(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error);
42
static int node_property_get_status(
43
                sd_bus *bus,
44
                const char *path,
45
                const char *interface,
46
                const char *property,
47
                sd_bus_message *reply,
48
                void *userdata,
49
                sd_bus_error *ret_error);
50
static int node_property_get_peer_ip(
51
                sd_bus *bus,
52
                const char *path,
53
                const char *interface,
54
                const char *property,
55
                sd_bus_message *reply,
56
                void *userdata,
57
                sd_bus_error *ret_error);
58

59
static const sd_bus_vtable internal_controller_controller_vtable[] = {
60
        SD_BUS_VTABLE_START(0),
61
        SD_BUS_METHOD("Register", "s", "", node_method_register, 0),
62
        SD_BUS_SIGNAL("Heartbeat", "", 0),
63
        SD_BUS_VTABLE_END
64
};
65

66
static const sd_bus_vtable node_vtable[] = {
67
        SD_BUS_VTABLE_START(0),
68
        SD_BUS_METHOD("ListUnits", "", UNIT_INFO_STRUCT_ARRAY_TYPESTRING, node_method_list_units, 0),
69
        SD_BUS_METHOD("ListUnitFiles", "", UNIT_FILE_INFO_STRUCT_ARRAY_TYPESTRING, node_method_list_unit_files, 0),
70
        SD_BUS_METHOD("GetUnitFileState", "s", "s", node_method_passthrough_to_agent, 0),
71
        SD_BUS_METHOD("StartUnit", "ss", "o", node_method_start_unit, 0),
72
        SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", node_method_passthrough_to_agent, 0),
73
        SD_BUS_METHOD("StopUnit", "ss", "o", node_method_stop_unit, 0),
74
        SD_BUS_METHOD("FreezeUnit", "s", "", node_method_passthrough_to_agent, 0),
75
        SD_BUS_METHOD("ThawUnit", "s", "", node_method_passthrough_to_agent, 0),
76
        SD_BUS_METHOD("RestartUnit", "ss", "o", node_method_restart_unit, 0),
77
        SD_BUS_METHOD("ReloadUnit", "ss", "o", node_method_reload_unit, 0),
78
        SD_BUS_METHOD("ResetFailed", "", "", node_method_passthrough_to_agent, 0),
79
        SD_BUS_METHOD("ResetFailedUnit", "s", "", node_method_passthrough_to_agent, 0),
80
        SD_BUS_METHOD("GetUnitProperties", "ss", "a{sv}", node_method_passthrough_to_agent, 0),
81
        SD_BUS_METHOD("GetUnitProperty", "sss", "v", node_method_passthrough_to_agent, 0),
82
        SD_BUS_METHOD("SetUnitProperties", "sba(sv)", "", node_method_set_unit_properties, 0),
83
        SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", node_method_passthrough_to_agent, 0),
84
        SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", node_method_passthrough_to_agent, 0),
85
        SD_BUS_METHOD("Reload", "", "", node_method_passthrough_to_agent, 0),
86
        SD_BUS_METHOD("KillUnit", "ssi", "", node_method_passthrough_to_agent, 0),
87
        SD_BUS_METHOD("SetLogLevel", "s", "", node_method_set_log_level, 0),
88
        SD_BUS_METHOD("GetDefaultTarget", "", "s", node_method_passthrough_to_agent, 0),
89
        SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", node_method_passthrough_to_agent, 0),
90
        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Node, name), SD_BUS_VTABLE_PROPERTY_CONST),
91
        SD_BUS_PROPERTY("Status", "s", node_property_get_status, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
92
        SD_BUS_PROPERTY("PeerIp", "s", node_property_get_peer_ip, 0, SD_BUS_VTABLE_PROPERTY_EXPLICIT),
93
        SD_BUS_PROPERTY("LastSeenTimestamp", "t", NULL, offsetof(Node, last_seen), SD_BUS_VTABLE_PROPERTY_EXPLICIT),
94
        SD_BUS_PROPERTY("LastSeenTimestampMonotonic",
95
                        "t",
96
                        NULL,
97
                        offsetof(Node, last_seen_monotonic),
98
                        SD_BUS_VTABLE_PROPERTY_EXPLICIT),
99
        SD_BUS_VTABLE_END
100
};
101

102
struct ProxyDependency {
103
        char *unit_name;
104
        int n_deps;
105
        LIST_FIELDS(ProxyDependency, deps);
106
};
107

108
static void proxy_dependency_free(struct ProxyDependency *dep) {
11✔
109
        free_and_null(dep->unit_name);
11✔
110
        free_and_null(dep);
11✔
111
}
11✔
112

113
typedef struct UnitSubscription UnitSubscription;
114

115
struct UnitSubscription {
116
        Subscription *sub;
117
        LIST_FIELDS(UnitSubscription, subs);
118
};
119

120
typedef struct {
121
        char *unit;
122
        LIST_HEAD(UnitSubscription, subs);
123
        bool loaded;
124
        UnitActiveState active_state;
125
        char *substate;
126
} UnitSubscriptions;
127

128
typedef struct {
129
        char *unit;
130
} UnitSubscriptionsKey;
131

132
static void unit_subscriptions_clear(void *item) {
29✔
133
        UnitSubscriptions *usubs = item;
29✔
134
        free_and_null(usubs->unit);
29✔
135
        free_and_null(usubs->substate);
29✔
136
        assert(LIST_IS_EMPTY(usubs->subs));
29✔
137
}
29✔
138

139
static uint64_t unit_subscriptions_hash(const void *item, uint64_t seed0, uint64_t seed1) {
906✔
140
        const UnitSubscriptions *usubs = item;
906✔
141
        return hashmap_sip(usubs->unit, strlen(usubs->unit), seed0, seed1);
906✔
142
}
143

144
static int unit_subscriptions_compare(const void *a, const void *b, UNUSED void *udata) {
518✔
145
        const UnitSubscriptions *usubs_a = a;
518✔
146
        const UnitSubscriptions *usubs_b = b;
518✔
147

148
        return strcmp(usubs_a->unit, usubs_b->unit);
518✔
149
}
150

151

152
Node *node_new(Controller *controller, const char *name) {
442✔
153
        _cleanup_node_ Node *node = malloc0(sizeof(Node));
442✔
154
        if (node == NULL) {
442✔
155
                return NULL;
156
        }
157

158
        node->ref_count = 1;
442✔
159
        node->controller = controller;
442✔
160
        LIST_INIT(nodes, node);
442✔
161
        LIST_HEAD_INIT(node->outstanding_requests);
442✔
162
        LIST_HEAD_INIT(node->proxy_monitors);
442✔
163
        LIST_HEAD_INIT(node->proxy_dependencies);
442✔
164

165
        node->unit_subscriptions = hashmap_new(
442✔
166
                        sizeof(UnitSubscriptions),
167
                        0,
168
                        0,
169
                        0,
170
                        unit_subscriptions_hash,
171
                        unit_subscriptions_compare,
172
                        unit_subscriptions_clear,
173
                        NULL);
174
        if (node->unit_subscriptions == NULL) {
442✔
175
                return NULL;
176
        }
177

178
        node->last_seen = 0;
442✔
179
        node->last_seen_monotonic = 0;
442✔
180

181
        node->name = NULL;
442✔
182
        if (name) {
442✔
183
                node->name = strdup(name);
289✔
184
                if (node->name == NULL) {
289✔
185
                        return NULL;
186
                }
187

188
                int r = assemble_object_path_string(NODE_OBJECT_PATH_PREFIX, name, &node->object_path);
289✔
189
                if (r < 0) {
289✔
190
                        return NULL;
191
                }
192
        }
193
        node->peer_ip = NULL;
442✔
194
        node->peer_selinux_context = NULL;
442✔
195

196
        node->is_shutdown = false;
442✔
197

198
        return steal_pointer(&node);
442✔
199
}
200

201
Node *node_ref(Node *node) {
131✔
202
        node->ref_count++;
131✔
203
        return node;
131✔
204
}
205

206
void node_unref(Node *node) {
573✔
207
        node->ref_count--;
573✔
208
        if (node->ref_count != 0) {
573✔
209
                return;
210
        }
211

212
        ProxyMonitor *proxy_monitor = NULL;
442✔
213
        ProxyMonitor *next_proxy_monitor = NULL;
442✔
214
        LIST_FOREACH_SAFE(monitors, proxy_monitor, next_proxy_monitor, node->proxy_monitors) {
442✔
215
                node_remove_proxy_monitor(node, proxy_monitor);
×
216
        }
217

218

219
        ProxyDependency *dep = NULL;
442✔
220
        ProxyDependency *next_dep = NULL;
442✔
221
        LIST_FOREACH_SAFE(deps, dep, next_dep, node->proxy_dependencies) {
442✔
222
                proxy_dependency_free(dep);
×
223
        }
224

225

226
        node_unset_agent_bus(node);
442✔
227
        sd_bus_slot_unrefp(&node->export_slot);
442✔
228

229
        hashmap_free(node->unit_subscriptions);
442✔
230

231
        free_and_null(node->name);
442✔
232
        free_and_null(node->object_path);
442✔
233
        free_and_null(node->peer_ip);
442✔
234
        free_and_null(node->required_selinux_context);
442✔
235
        free(node);
442✔
236
}
237

238
void node_shutdown(Node *node) {
14✔
239
        AgentRequest *req = NULL;
14✔
240
        AgentRequest *next_req = NULL;
14✔
241
        node->is_shutdown = true;
14✔
242
        LIST_FOREACH_SAFE(outstanding_requests, req, next_req, node->outstanding_requests) {
14✔
243
                agent_request_cancel(req);
×
244
        }
245
}
14✔
246

247
bool node_set_required_selinux_context(Node *node, const char *selinux_context) {
×
248
        node->required_selinux_context = strdup(selinux_context);
×
249
        if (node->required_selinux_context == NULL) {
×
250
                return false;
×
251
        }
252
        return true;
253
}
254

255
bool node_export(Node *node) {
289✔
256
        Controller *controller = node->controller;
289✔
257

258
        assert(node->name != NULL);
289✔
259

260
        int r = sd_bus_add_object_vtable(
578✔
261
                        controller->api_bus,
262
                        &node->export_slot,
263
                        node->object_path,
289✔
264
                        NODE_INTERFACE,
265
                        node_vtable,
266
                        node);
267
        if (r < 0) {
289✔
268
                bc_log_errorf("Failed to add node vtable: %s", strerror(-r));
×
269
                return false;
×
270
        }
271

272
        return true;
273
}
274

275
static int debug_messages_handler(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
276
        Node *node = userdata;
277
        if (node->name) {
278
                bc_log_infof("Incoming message from node '%s' (fd %d): path: %s, iface: %s, member: %s, signature: '%s'",
279
                             node->name,
280
                             sd_bus_get_fd(node->agent_bus),
281
                             sd_bus_message_get_path(m),
282
                             sd_bus_message_get_interface(m),
283
                             sd_bus_message_get_member(m),
284
                             sd_bus_message_get_signature(m, true));
285
        } else {
286
                bc_log_infof("Incoming message from node fd %d: path: %s, iface: %s, member: %s, signature: '%s'",
287
                             sd_bus_get_fd(node->agent_bus),
288
                             sd_bus_message_get_path(m),
289
                             sd_bus_message_get_interface(m),
290
                             sd_bus_message_get_member(m),
291
                             sd_bus_message_get_signature(m, true));
292
        }
293
        return 0;
294
}
295

296
bool node_has_agent(Node *node) {
1,428✔
297
        return node->agent_bus != NULL;
1,428✔
298
}
299

300
bool node_is_online(Node *node) {
445✔
301
        return node && node->name && node_has_agent(node);
445✔
302
}
303

304

305
static uint64_t subscription_hashmap_hash(const void *item, UNUSED uint64_t seed0, UNUSED uint64_t seed1) {
330✔
306
        const Subscription * const *subscriptionp = item;
330✔
307
        return (uint64_t) ((uintptr_t) *subscriptionp);
330✔
308
}
309

310
static int subscription_hashmap_compare(const void *a, const void *b, UNUSED void *udata) {
×
311
        const Subscription * const *subscription_a_p = a;
×
312
        const Subscription * const *subscription_b_p = b;
×
313
        if ((*subscription_a_p)->monitor == (*subscription_b_p)->monitor) {
×
314
                return 0;
×
315
        }
316
        return 1;
317
}
318

319
static struct hashmap *node_compute_unique_monitor_subscriptions(Node *node, const char *unit) {
330✔
320
        struct hashmap *unique_subs = hashmap_new(
330✔
321
                        sizeof(void *), 0, 0, 0, subscription_hashmap_hash, subscription_hashmap_compare, NULL, NULL);
322
        if (unique_subs == NULL) {
330✔
323
                return NULL;
330✔
324
        }
325

326
        const UnitSubscriptionsKey key = { (char *) unit };
330✔
327
        const UnitSubscriptions *usubs = hashmap_get(node->unit_subscriptions, &key);
330✔
328
        if (usubs != NULL) {
330✔
329
                UnitSubscription *usub = NULL;
292✔
330
                UnitSubscription *next_usub = NULL;
292✔
331
                LIST_FOREACH_SAFE(subs, usub, next_usub, usubs->subs) {
584✔
332
                        Subscription *sub = usub->sub;
292✔
333
                        hashmap_set(unique_subs, &sub);
292✔
334
                        if (hashmap_oom(unique_subs)) {
292✔
335
                                bc_log_error("Failed to compute vector of unique monitors, OOM");
×
336

337
                                hashmap_free(unique_subs);
×
338
                                unique_subs = NULL;
×
339
                                return NULL;
×
340
                        }
341
                }
342
        }
343

344
        /* Only check for wildcards if the unit itself is not one. */
345
        if (!streq(unit, SYMBOL_WILDCARD)) {
330✔
346
                const UnitSubscriptionsKey wildcard_key = { (char *) SYMBOL_WILDCARD };
322✔
347
                const UnitSubscriptions *usubs_wildcard = hashmap_get(node->unit_subscriptions, &wildcard_key);
322✔
348
                if (usubs_wildcard != NULL) {
322✔
349
                        UnitSubscription *usub = NULL;
38✔
350
                        UnitSubscription *next_usub = NULL;
38✔
351
                        LIST_FOREACH_SAFE(subs, usub, next_usub, usubs_wildcard->subs) {
76✔
352
                                Subscription *sub = usub->sub;
38✔
353
                                hashmap_set(unique_subs, &sub);
38✔
354
                                if (hashmap_oom(unique_subs)) {
38✔
355
                                        bc_log_error("Failed to compute vector of unique monitors, OOM");
×
356

357
                                        hashmap_free(unique_subs);
×
358
                                        unique_subs = NULL;
×
359
                                        return NULL;
×
360
                                }
361
                        }
362
                }
363
        }
364

365
        return unique_subs;
366
}
367

368

369
static int node_match_job_state_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
5✔
370
        Node *node = userdata;
5✔
371
        Controller *controller = node->controller;
5✔
372
        uint32_t bc_job_id = 0;
5✔
373
        const char *state = NULL;
5✔
374

375
        int r = sd_bus_message_read(m, "us", &bc_job_id, &state);
5✔
376
        if (r < 0) {
5✔
377
                bc_log_errorf("Invalid JobStateChange signal: %s", strerror(-r));
×
378
                return 0;
×
379
        }
380

381
        controller_job_state_changed(controller, bc_job_id, state);
5✔
382
        return 1;
383
}
384

385
static int node_match_unit_properties_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
221✔
386
        Node *node = userdata;
221✔
387
        const char *unit = NULL;
221✔
388
        const char *interface = NULL;
221✔
389

390
        int r = sd_bus_message_read(m, "ss", &unit, &interface);
221✔
391
        if (r >= 0) {
221✔
392
                r = sd_bus_message_rewind(m, false);
221✔
393
        }
394
        if (r < 0) {
221✔
395
                bc_log_error("Invalid UnitPropertiesChanged signal");
×
396
                return 0;
×
397
        }
398

399
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
221✔
400
        if (unique_subs != NULL) {
221✔
401
                Subscription **subp = NULL;
221✔
402
                size_t i = 0;
221✔
403
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
663✔
404
                        Subscription *sub = *subp;
221✔
405
                        int r = sub->handle_unit_property_changed(sub->monitor, node->name, unit, interface, m);
221✔
406
                        if (r < 0) {
221✔
407
                                bc_log_error("Failed to emit UnitPropertyChanged signal");
×
408
                        }
409
                }
410
                hashmap_free(unique_subs);
221✔
411
        }
412

413
        return 1;
414
}
415

416

417
static int node_match_unit_new(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
36✔
418
        Node *node = userdata;
36✔
419
        const char *unit = NULL;
36✔
420
        const char *reason = NULL;
36✔
421

422
        int r = sd_bus_message_read(m, "ss", &unit, &reason);
36✔
423
        if (r < 0) {
36✔
424
                bc_log_errorf("Invalid UnitNew signal: %s", strerror(-r));
×
425
                return 0;
×
426
        }
427

428
        const UnitSubscriptionsKey key = { (char *) unit };
36✔
429
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
36✔
430
        if (usubs != NULL) {
36✔
431
                usubs->loaded = true;
34✔
432
                if (is_wildcard(unit)) {
34✔
433
                        usubs->active_state = UNIT_ACTIVE;
6✔
434
                        free(usubs->substate);
6✔
435
                        usubs->substate = strdup("running");
6✔
436
                }
437
        }
438

439
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
36✔
440
        if (unique_subs != NULL) {
36✔
441
                Subscription **subp = NULL;
36✔
442
                size_t i = 0;
36✔
443
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
108✔
444
                        Subscription *sub = *subp;
36✔
445
                        int r = sub->handle_unit_new(sub->monitor, node->name, unit, reason);
36✔
446
                        if (r < 0) {
36✔
447
                                bc_log_errorf("Failed to emit UnitNew signal: %s", strerror(-r));
×
448
                        }
449
                }
450
                hashmap_free(unique_subs);
36✔
451
        }
452

453
        return 1;
454
}
455

456
static int node_match_unit_state_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
51✔
457
        Node *node = userdata;
51✔
458
        const char *unit = NULL;
51✔
459
        const char *active_state = NULL;
51✔
460
        const char *substate = NULL;
51✔
461
        const char *reason = NULL;
51✔
462

463
        int r = sd_bus_message_read(m, "ssss", &unit, &active_state, &substate, &reason);
51✔
464
        if (r < 0) {
51✔
465
                bc_log_errorf("Invalid UnitStateChanged signal: %s", strerror(-r));
×
466
                return 0;
×
467
        }
468

469
        const UnitSubscriptionsKey key = { (char *) unit };
51✔
470
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
51✔
471
        if (usubs != NULL) {
51✔
472
                usubs->loaded = true;
47✔
473
                usubs->active_state = active_state_from_string(active_state);
47✔
474
                free(usubs->substate);
47✔
475
                usubs->substate = strdup(substate);
47✔
476
        }
477

478
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
51✔
479
        if (unique_subs != NULL) {
51✔
480
                Subscription **subp = NULL;
51✔
481
                size_t i = 0;
51✔
482
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
153✔
483
                        Subscription *sub = *subp;
51✔
484
                        int r = sub->handle_unit_state_changed(
102✔
485
                                        sub->monitor, node->name, unit, active_state, substate, reason);
51✔
486
                        if (r < 0) {
51✔
487
                                bc_log_errorf("Failed to emit UnitStateChanged signal: %s", strerror(-r));
×
488
                        }
489
                }
490
                hashmap_free(unique_subs);
51✔
491
        }
492

493
        return 1;
494
}
495

496
static int node_match_unit_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
20✔
497
        Node *node = userdata;
20✔
498
        const char *unit = NULL;
20✔
499

500
        int r = sd_bus_message_read(m, "s", &unit);
20✔
501
        if (r < 0) {
20✔
502
                bc_log_errorf("Invalid UnitRemoved signal: %s", strerror(-r));
×
503
                return 0;
×
504
        }
505

506
        const UnitSubscriptionsKey key = { (char *) unit };
20✔
507
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
20✔
508
        if (usubs != NULL) {
20✔
509
                usubs->loaded = false;
18✔
510
        }
511

512
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
20✔
513
        if (unique_subs != NULL) {
20✔
514
                Subscription **subp = NULL;
20✔
515
                size_t i = 0;
20✔
516
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
60✔
517
                        Subscription *sub = *subp;
20✔
518
                        int r = sub->handle_unit_removed(sub->monitor, node->name, unit, "real");
20✔
519
                        if (r < 0) {
20✔
520
                                bc_log_errorf("Failed to emit UnitRemoved signal: %s", strerror(-r));
×
521
                        }
522
                }
523
                hashmap_free(unique_subs);
20✔
524
        }
525

526
        return 1;
527
}
528

529
static int node_match_job_done(UNUSED sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *error) {
62✔
530
        Node *node = userdata;
62✔
531
        Controller *controller = node->controller;
62✔
532
        uint32_t bc_job_id = 0;
62✔
533
        const char *result = NULL;
62✔
534

535
        int r = sd_bus_message_read(m, "us", &bc_job_id, &result);
62✔
536
        if (r < 0) {
62✔
537
                bc_log_errorf("Invalid JobDone signal: %s", strerror(-r));
×
538
                return 0;
×
539
        }
540

541
        controller_finish_job(controller, bc_job_id, result);
62✔
542
        return 1;
543
}
544

545
static int node_match_heartbeat(UNUSED sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
38✔
546
        Node *node = userdata;
38✔
547
        uint64_t now = 0;
38✔
548
        uint64_t now_monotonic = 0;
38✔
549

550
        now = get_time_micros();
38✔
551
        if (now == USEC_INFINITY) {
38✔
552
                bc_log_error("Failed to get current time on heartbeat");
×
553
                return 0;
×
554
        }
555

556
        now_monotonic = get_time_micros_monotonic();
38✔
557
        if (now_monotonic == USEC_INFINITY) {
38✔
558
                bc_log_error("Failed to get current monotonic time on heartbeat");
×
559
                return 0;
×
560
        }
561

562
        node->last_seen = now;
38✔
563
        node->last_seen_monotonic = now_monotonic;
38✔
564
        return 1;
38✔
565
}
566

567
static ProxyMonitor *node_find_proxy_monitor(Node *node, const char *target_node_name, const char *unit_name) {
23✔
568
        ProxyMonitor *proxy_monitor = NULL;
23✔
569
        LIST_FOREACH(monitors, proxy_monitor, node->proxy_monitors) {
23✔
570
                if (streq(proxy_monitor->target_node->name, target_node_name) &&
10✔
571
                    streq(proxy_monitor->unit_name, unit_name)) {
10✔
572
                        return proxy_monitor;
573
                }
574
        }
575

576
        return NULL;
577
}
578

579
static int node_on_match_proxy_new(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
13✔
580
        Node *node = userdata;
13✔
581
        Controller *controller = node->controller;
13✔
582

583
        const char *target_node_name = NULL;
13✔
584
        const char *unit_name = NULL;
13✔
585
        const char *proxy_object_path = NULL;
13✔
586

587
        int r = sd_bus_message_read(m, "sso", &target_node_name, &unit_name, &proxy_object_path);
13✔
588
        if (r < 0) {
13✔
589
                bc_log_errorf("Invalid arguments in ProxyNew signal: %s", strerror(-r));
×
590
                return r;
×
591
        }
592
        bc_log_infof("Node '%s' registered new proxy for unit '%s' on node '%s'",
13✔
593
                     node->name,
594
                     unit_name,
595
                     target_node_name);
596

597
        _cleanup_proxy_monitor_ ProxyMonitor *monitor = proxy_monitor_new(
26✔
598
                        node, target_node_name, unit_name, proxy_object_path);
599
        if (monitor == NULL) {
13✔
600
                bc_log_error("Failed to create proxy monitor, OOM");
×
601
                return -ENOMEM;
602
        }
603

604
        Node *target_node = controller_find_node(controller, target_node_name);
13✔
605
        if (target_node == NULL) {
13✔
606
                bc_log_error("Proxy requested for non-existing node");
1✔
607
                proxy_monitor_send_error(monitor, "No such node");
1✔
608
                return 0;
609
        }
610

611
        ProxyMonitor *old_monitor = node_find_proxy_monitor(node, target_node_name, unit_name);
12✔
612
        if (old_monitor != NULL) {
12✔
613
                bc_log_warnf("Proxy for '%s' (on '%s') requested, but old proxy already exists, removing it",
×
614
                             unit_name,
615
                             target_node_name);
616
                node_remove_proxy_monitor(node, old_monitor);
×
617
        }
618

619
        r = proxy_monitor_set_target_node(monitor, target_node);
12✔
620
        if (r < 0) {
12✔
621
                bc_log_errorf("Failed to add proxy dependency: %s", strerror(-r));
×
622
                proxy_monitor_send_error(monitor, "Failed to add proxy dependency");
×
623
                return 0;
624
        }
625

626
        /* We now have a valid monitor, add it to the list and enable monitor.
627
           From this point we should not send errors. */
628
        controller_add_subscription(controller, monitor->subscription);
12✔
629
        LIST_APPEND(monitors, node->proxy_monitors, proxy_monitor_ref(monitor));
12✔
630

631
        /* TODO: What about !!node_is_online(target_node) ? Tell now or wait for it to connect? */
632

633
        return 0;
634
}
635

636
void node_remove_proxy_monitor(Node *node, ProxyMonitor *monitor) {
12✔
637
        Controller *controller = node->controller;
12✔
638

639
        proxy_monitor_close(monitor);
12✔
640

641
        controller_remove_subscription(controller, monitor->subscription);
12✔
642
        LIST_REMOVE(monitors, node->proxy_monitors, monitor);
12✔
643

644
        proxy_monitor_unref(monitor);
12✔
645
}
12✔
646

647
static int node_on_match_proxy_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
11✔
648
        Node *node = userdata;
11✔
649
        const char *target_node_name = NULL;
11✔
650
        const char *unit_name = NULL;
11✔
651

652
        int r = sd_bus_message_read(m, "ss", &target_node_name, &unit_name);
11✔
653
        if (r < 0) {
11✔
654
                bc_log_errorf("Invalid arguments in ProxyRemoved signal: %s", strerror(-r));
×
655
                return r;
×
656
        }
657
        bc_log_infof("Node '%s' unregistered proxy for unit '%s' on node '%s'",
11✔
658
                     node->name,
659
                     unit_name,
660
                     target_node_name);
661

662
        ProxyMonitor *proxy_monitor = node_find_proxy_monitor(node, target_node_name, unit_name);
11✔
663
        if (proxy_monitor == NULL) {
11✔
664
                bc_log_error("Got ProxyRemoved for unknown monitor");
1✔
665
                return 0;
1✔
666
        }
667

668
        node_remove_proxy_monitor(node, proxy_monitor);
10✔
669
        return 0;
670
}
671

672
bool node_set_agent_bus(Node *node, sd_bus *bus) {
304✔
673
        int r = 0;
304✔
674

675
        if (node->agent_bus != NULL) {
304✔
676
                bc_log_error("Error: Trying to add two agents for a node");
×
677
                return false;
×
678
        }
679

680
        node->agent_bus = sd_bus_ref(bus);
304✔
681

682
        // If getting peer IP fails, only log and proceed as normal.
683
        _cleanup_free_ char *peer_ip = NULL;
304✔
684
        uint16_t peer_port = 0;
304✔
685
        r = get_peer_ip_address(node->agent_bus, &peer_ip, &peer_port);
304✔
686
        if (r < 0 && r != -EINVAL) {
304✔
687
                bc_log_errorf("Failed to get peer IP: %s", strerror(-r));
×
688
        } else {
689
                node->peer_ip = steal_pointer(&peer_ip);
304✔
690
        }
691

692
#ifdef CONFIG_H_USE_SELINUX
693
        char *peercon = NULL;
304✔
694
        if (getpeercon(sd_bus_get_fd(bus), &peercon) == 0) {
304✔
695
                node->peer_selinux_context = parse_selinux_type(peercon);
8✔
696
                freecon(peercon);
8✔
697
                if (node->peer_selinux_context == NULL) {
8✔
698
                        bc_log_errorf("Failed to parse peer selinux type '%s'", peercon);
8✔
699
                }
700
        }
701
#endif
702

703
        if (node->name == NULL) {
304✔
704
                // We only connect to this on the unnamed nodes so register
705
                // can be called. We can't reconnect it during migration.
706
                r = sd_bus_add_object_vtable(
153✔
707
                                bus,
708
                                &node->internal_controller_slot,
709
                                INTERNAL_CONTROLLER_OBJECT_PATH,
710
                                INTERNAL_CONTROLLER_INTERFACE,
711
                                internal_controller_controller_vtable,
712
                                node);
713
                if (r < 0) {
153✔
714
                        node_unset_agent_bus(node);
×
715
                        bc_log_errorf("Failed to add peer bus vtable: %s", strerror(-r));
×
716
                        return false;
717
                }
718
        } else {
719
                // Only listen to signals on named nodes
720
                r = sd_bus_match_signal(
151✔
721
                                bus,
722
                                NULL,
723
                                NULL,
724
                                INTERNAL_AGENT_OBJECT_PATH,
725
                                INTERNAL_AGENT_INTERFACE,
726
                                "JobDone",
727
                                node_match_job_done,
728
                                node);
729
                if (r < 0) {
151✔
730
                        bc_log_errorf("Failed to add JobDone peer bus match: %s", strerror(-r));
×
731
                        return false;
732
                }
733

734
                r = sd_bus_match_signal(
151✔
735
                                bus,
736
                                NULL,
737
                                NULL,
738
                                INTERNAL_AGENT_OBJECT_PATH,
739
                                INTERNAL_AGENT_INTERFACE,
740
                                "JobStateChanged",
741
                                node_match_job_state_changed,
742
                                node);
743
                if (r < 0) {
151✔
744
                        bc_log_errorf("Failed to add JobStateChanged peer bus match: %s", strerror(-r));
×
745
                        return false;
746
                }
747

748
                r = sd_bus_match_signal(
151✔
749
                                bus,
750
                                NULL,
751
                                NULL,
752
                                INTERNAL_AGENT_OBJECT_PATH,
753
                                INTERNAL_AGENT_INTERFACE,
754
                                "UnitPropertiesChanged",
755
                                node_match_unit_properties_changed,
756
                                node);
757
                if (r < 0) {
151✔
758
                        bc_log_errorf("Failed to add UnitPropertiesChanged peer bus match: %s", strerror(-r));
×
759
                        return false;
760
                }
761

762
                r = sd_bus_match_signal(
151✔
763
                                bus,
764
                                NULL,
765
                                NULL,
766
                                INTERNAL_AGENT_OBJECT_PATH,
767
                                INTERNAL_AGENT_INTERFACE,
768
                                "UnitNew",
769
                                node_match_unit_new,
770
                                node);
771
                if (r < 0) {
151✔
772
                        bc_log_errorf("Failed to add UnitNew peer bus match: %s", strerror(-r));
×
773
                        return false;
774
                }
775

776
                r = sd_bus_match_signal(
151✔
777
                                bus,
778
                                NULL,
779
                                NULL,
780
                                INTERNAL_AGENT_OBJECT_PATH,
781
                                INTERNAL_AGENT_INTERFACE,
782
                                "UnitStateChanged",
783
                                node_match_unit_state_changed,
784
                                node);
785
                if (r < 0) {
151✔
786
                        bc_log_errorf("Failed to add UnitStateChanged peer bus match: %s", strerror(-r));
×
787
                        return false;
788
                }
789

790
                r = sd_bus_match_signal(
151✔
791
                                bus,
792
                                NULL,
793
                                NULL,
794
                                INTERNAL_AGENT_OBJECT_PATH,
795
                                INTERNAL_AGENT_INTERFACE,
796
                                "UnitRemoved",
797
                                node_match_unit_removed,
798
                                node);
799
                if (r < 0) {
151✔
800
                        bc_log_errorf("Failed to add UnitRemoved peer bus match: %s", strerror(-r));
×
801
                        return false;
802
                }
803

804
                r = sd_bus_match_signal(
151✔
805
                                bus,
806
                                NULL,
807
                                NULL,
808
                                INTERNAL_AGENT_OBJECT_PATH,
809
                                INTERNAL_AGENT_INTERFACE,
810
                                "ProxyNew",
811
                                node_on_match_proxy_new,
812
                                node);
813
                if (r < 0) {
151✔
814
                        bc_log_errorf("Failed to add ProxyNew peer bus match: %s", strerror(-r));
×
815
                        return false;
816
                }
817

818
                r = sd_bus_match_signal(
151✔
819
                                bus,
820
                                NULL,
821
                                NULL,
822
                                INTERNAL_AGENT_OBJECT_PATH,
823
                                INTERNAL_AGENT_INTERFACE,
824
                                "ProxyRemoved",
825
                                node_on_match_proxy_removed,
826
                                node);
827
                if (r < 0) {
151✔
828
                        bc_log_errorf("Failed to add ProxyNew peer bus match: %s", strerror(-r));
×
829
                        return false;
830
                }
831

832
                r = sd_bus_emit_properties_changed(
302✔
833
                                node->controller->api_bus, node->object_path, NODE_INTERFACE, "Status", NULL);
151✔
834
                if (r < 0) {
151✔
835
                        bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
836
                }
837

838
                r = sd_bus_match_signal(
151✔
839
                                bus,
840
                                NULL,
841
                                NULL,
842
                                INTERNAL_AGENT_OBJECT_PATH,
843
                                INTERNAL_AGENT_INTERFACE,
844
                                AGENT_HEARTBEAT_SIGNAL_NAME,
845
                                node_match_heartbeat,
846
                                node);
847
                if (r < 0) {
151✔
848
                        bc_log_errorf("Failed to add heartbeat signal match: %s", strerror(-r));
×
849
                        return false;
850
                }
851
        }
852

853
        r = sd_bus_match_signal_async(
304✔
854
                        bus,
855
                        &node->disconnect_slot,
856
                        "org.freedesktop.DBus.Local",
857
                        "/org/freedesktop/DBus/Local",
858
                        "org.freedesktop.DBus.Local",
859
                        "Disconnected",
860
                        node_disconnected,
861
                        NULL,
862
                        node);
863
        if (r < 0) {
304✔
864
                node_unset_agent_bus(node);
×
865
                bc_log_errorf("Failed to request match for Disconnected message: %s", strerror(-r));
×
866
                return false;
867
        }
868

869
        if (DEBUG_AGENT_MESSAGES) {
304✔
870
                sd_bus_add_filter(bus, NULL, debug_messages_handler, node);
871
        }
872

873

874
        /* Register any active subscriptions with new agent */
875
        node_send_agent_subscribe_all(node);
304✔
876

877
        /* Register any active dependencies with new agent */
878
        node_start_proxy_dependency_all(node);
304✔
879

880
        return true;
881
}
882

883
void node_unset_agent_bus(Node *node) {
730✔
884
        bool was_online = node->name && node_has_agent(node);
730✔
885

886
        sd_bus_slot_unrefp(&node->disconnect_slot);
730✔
887
        node->disconnect_slot = NULL;
730✔
888

889
        sd_bus_slot_unrefp(&node->internal_controller_slot);
730✔
890
        node->internal_controller_slot = NULL;
730✔
891

892
        sd_bus_slot_unrefp(&node->metrics_matching_slot);
730✔
893
        node->metrics_matching_slot = NULL;
730✔
894

895
        sd_bus_unrefp(&node->agent_bus);
730✔
896
        node->agent_bus = NULL;
730✔
897

898
        free_and_null(node->peer_selinux_context);
730✔
899
        free_and_null(node->peer_ip);
730✔
900

901
        if (was_online) {
730✔
902
                int r = sd_bus_emit_properties_changed(
302✔
903
                                node->controller->api_bus, node->object_path, NODE_INTERFACE, "Status", NULL);
151✔
904
                if (r < 0) {
151✔
905
                        bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
906
                }
907
        }
908
}
730✔
909

910
/* org.eclipse.bluechi.internal.Controller.Register(in s name)) */
911
static int node_method_register(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
152✔
912
        Node *node = userdata;
152✔
913
        Controller *controller = node->controller;
152✔
914
        char *name = NULL;
152✔
915
        _cleanup_free_ char *description = NULL;
152✔
916

917
        /* Once we're not anonymous, don't allow register calls */
918
        if (node->name != NULL) {
152✔
919
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ADDRESS_IN_USE, "Can't register twice");
×
920
        }
921

922
        /* Read the parameters */
923
        int r = sd_bus_message_read(m, "s", &name);
152✔
924
        if (r < 0) {
152✔
925
                bc_log_errorf("Failed to parse parameters: %s", strerror(-r));
×
926
                return r;
927
        }
928

929
        Node *named_node = controller_find_node(controller, name);
152✔
930
        if (named_node == NULL) {
152✔
931
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_SERVICE_UNKNOWN, "Unexpected node name");
1✔
932
        }
933

934
        if (named_node->required_selinux_context &&
151✔
935
            (node->peer_selinux_context == NULL ||
×
936
             !streq(node->peer_selinux_context, named_node->required_selinux_context))) {
×
937
                bc_log_errorf("Node tried to register as '%s' with wrong selinux context '%s', expected '%s'",
×
938
                              name,
939
                              node->peer_selinux_context ? node->peer_selinux_context : "(missing)",
940
                              named_node->required_selinux_context);
941

942
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Node name not allowed");
×
943
        }
944

945
        if (node_has_agent(named_node)) {
151✔
946
                return sd_bus_reply_method_errorf(
×
947
                                m, SD_BUS_ERROR_ADDRESS_IN_USE, "The node is already connected");
948
        }
949

950
        named_node->last_seen = get_time_micros();
151✔
951
        named_node->last_seen_monotonic = get_time_micros_monotonic();
151✔
952

953
        r = asprintf(&description, "node-%s", name);
151✔
954
        if (r >= 0) {
151✔
955
                (void) sd_bus_set_description(node->agent_bus, description);
151✔
956
        }
957

958
        /* Migrate the agent connection to the named node */
959
        _cleanup_sd_bus_ sd_bus *agent_bus = sd_bus_ref(node->agent_bus);
303✔
960
        if (!node_set_agent_bus(named_node, agent_bus)) {
151✔
961
                return sd_bus_reply_method_errorf(
×
962
                                m, SD_BUS_ERROR_FAILED, "Internal error: Couldn't set agent bus");
963
        }
964

965
        if (controller->metrics_enabled) {
151✔
966
                node_enable_metrics(named_node);
×
967
        }
968

969
        node_unset_agent_bus(node);
151✔
970

971
        /* update number of online nodes and check the new system state */
972
        controller_check_system_status(controller, controller->number_of_nodes_online++);
151✔
973

974
        bc_log_infof("Registered managed node from fd %d as '%s'", sd_bus_get_fd(agent_bus), name);
151✔
975

976
        return sd_bus_reply_method_return(m, "");
151✔
977
}
978

979
static int node_disconnected(UNUSED sd_bus_message *message, void *userdata, UNUSED sd_bus_error *error) {
138✔
980
        Node *node = userdata;
138✔
981

982
        node_disconnect(node);
138✔
983

984
        return 0;
138✔
985
}
986

987
void node_disconnect(Node *node) {
139✔
988
        Controller *controller = node->controller;
139✔
989
        void *item = NULL;
139✔
990
        size_t i = 0;
139✔
991

992
        /* Send virtual unit remove and state change for any reported loaded units */
993
        while (hashmap_iter(node->unit_subscriptions, &i, &item)) {
140✔
994
                UnitSubscriptions *usubs = item;
1✔
995
                bool send_state_change = false;
1✔
996

997
                if (!usubs->loaded) {
1✔
998
                        continue;
×
999
                }
1000

1001
                if (usubs->active_state >= 0 && usubs->active_state != UNIT_INACTIVE) {
1✔
1002
                        /* We previously reported an not-inactive valid state, send a virtual inactive state */
1003
                        usubs->active_state = UNIT_INACTIVE;
1✔
1004
                        free(usubs->substate);
1✔
1005
                        usubs->substate = strdup("agent-offline");
1✔
1006
                        send_state_change = true;
1✔
1007
                }
1008

1009
                usubs->loaded = false;
1✔
1010

1011
                int r = 0;
1✔
1012
                if (send_state_change) {
×
1013
                        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(
2✔
1014
                                        node, usubs->unit);
1✔
1015
                        if (unique_subs != NULL) {
1✔
1016

1017
                                Subscription **subp = NULL;
1✔
1018
                                size_t s = 0;
1✔
1019
                                while (hashmap_iter(unique_subs, &s, (void **) &subp)) {
3✔
1020
                                        Subscription *sub = *subp;
1✔
1021
                                        r = sub->handle_unit_state_changed(
3✔
1022
                                                        sub->monitor,
1023
                                                        node->name,
1✔
1024
                                                        usubs->unit,
1✔
1025
                                                        active_state_to_string(usubs->active_state),
1026
                                                        usubs->substate,
1✔
1027
                                                        "virtual");
1028
                                        if (r < 0) {
1✔
1029
                                                bc_log_error("Failed to emit UnitStateChanged signal");
×
1030
                                        }
1031
                                }
1032
                                hashmap_free(unique_subs);
1✔
1033
                        }
1034
                }
1035

1036

1037
                struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, usubs->unit);
1✔
1038
                if (unique_subs != NULL) {
1✔
1039

1040
                        Subscription **subp = NULL;
1✔
1041
                        size_t s = 0;
1✔
1042
                        while (hashmap_iter(unique_subs, &s, (void **) &subp)) {
3✔
1043
                                Subscription *sub = *subp;
1✔
1044
                                r = sub->handle_unit_removed(sub->monitor, node->name, usubs->unit, "virtual");
1✔
1045
                                if (r < 0) {
1✔
1046
                                        bc_log_error("Failed to emit UnitRemoved signal");
×
1047
                                }
1048
                        }
1049
                        hashmap_free(unique_subs);
1✔
1050
                }
1051
        }
1052

1053
        ProxyMonitor *proxy_monitor = NULL;
139✔
1054
        ProxyMonitor *next_proxy_monitor = NULL;
139✔
1055
        LIST_FOREACH_SAFE(monitors, proxy_monitor, next_proxy_monitor, node->proxy_monitors) {
141✔
1056
                node_remove_proxy_monitor(node, proxy_monitor);
2✔
1057
        }
1058

1059
        /* Remove anonymous nodes when they disconnect */
1060
        if (node->name == NULL) {
139✔
1061
                bc_log_info("Anonymous node disconnected");
2✔
1062
                controller_remove_node(controller, node);
2✔
1063
        } else {
1064
                bc_log_infof("Node '%s' disconnected", node->name);
137✔
1065
                /* Remove all jobs associated with the registered node that got disconnected. */
1066
                if (!LIST_IS_EMPTY(controller->jobs)) {
137✔
1067
                        Job *job = NULL;
1068
                        Job *next_job = NULL;
4✔
1069
                        LIST_FOREACH_SAFE(jobs, job, next_job, controller->jobs) {
4✔
1070
                                if (strcmp(job->node->name, node->name) == 0) {
2✔
1071
                                        bc_log_debugf("Removing job %d from node %s", job->id, job->node->name);
2✔
1072
                                        LIST_REMOVE(jobs, controller->jobs, job);
2✔
1073
                                        job_unref(job);
2✔
1074
                                }
1075
                        }
1076
                }
1077
                node_unset_agent_bus(node);
137✔
1078

1079
                /* update number of online nodes and check the new system state */
1080
                controller_check_system_status(controller, controller->number_of_nodes_online--);
137✔
1081
        }
1082
}
139✔
1083

1084
const char *node_get_status(Node *node) {
352✔
1085
        if (node_has_agent(node)) {
352✔
1086
                return "online";
186✔
1087
        }
1088
        return "offline";
1089
}
1090

1091
static int node_property_get_status(
326✔
1092
                UNUSED sd_bus *bus,
1093
                UNUSED const char *path,
1094
                UNUSED const char *interface,
1095
                UNUSED const char *property,
1096
                sd_bus_message *reply,
1097
                void *userdata,
1098
                UNUSED sd_bus_error *ret_error) {
1099
        Node *node = userdata;
326✔
1100
        return sd_bus_message_append(reply, "s", node_get_status(node));
326✔
1101
}
1102

1103
static int node_property_get_peer_ip(
3✔
1104
                UNUSED sd_bus *bus,
1105
                UNUSED const char *path,
1106
                UNUSED const char *interface,
1107
                UNUSED const char *property,
1108
                sd_bus_message *reply,
1109
                void *userdata,
1110
                UNUSED sd_bus_error *ret_error) {
1111
        Node *node = userdata;
3✔
1112
        return sd_bus_message_append(reply, "s", node->peer_ip);
3✔
1113
}
1114

1115
AgentRequest *agent_request_ref(AgentRequest *req) {
115✔
1116
        req->ref_count++;
115✔
1117
        return req;
115✔
1118
}
1119

1120
void agent_request_unref(AgentRequest *req) {
230✔
1121
        req->ref_count--;
230✔
1122
        if (req->ref_count != 0) {
230✔
1123
                return;
1124
        }
1125

1126
        if (req->userdata && req->free_userdata) {
115✔
1127
                req->free_userdata(req->userdata);
111✔
1128
        }
1129
        sd_bus_slot_unrefp(&req->slot);
115✔
1130
        sd_bus_message_unrefp(&req->message);
115✔
1131

1132
        Node *node = req->node;
115✔
1133
        LIST_REMOVE(outstanding_requests, node->outstanding_requests, req);
115✔
1134
        node_unref(req->node);
115✔
1135
        free(req);
115✔
1136
}
1137

1138
int node_create_request(
115✔
1139
                AgentRequest **ret,
1140
                Node *node,
1141
                const char *method,
1142
                agent_request_response_t cb,
1143
                void *userdata,
1144
                free_func_t free_userdata) {
1145
        AgentRequest *req = malloc0(sizeof(AgentRequest));
115✔
1146
        if (req == NULL) {
115✔
1147
                return -ENOMEM;
1148
        }
1149

1150
        int r = sd_bus_message_new_method_call(
115✔
1151
                        node->agent_bus,
1152
                        &req->message,
1153
                        BC_AGENT_DBUS_NAME,
1154
                        INTERNAL_AGENT_OBJECT_PATH,
1155
                        INTERNAL_AGENT_INTERFACE,
1156
                        method);
1157
        if (r < 0) {
115✔
1158
                free(req);
×
1159
                req = NULL;
×
1160
                return r;
×
1161
        }
1162

1163
        req->ref_count = 1;
115✔
1164
        req->node = node_ref(node);
115✔
1165
        LIST_INIT(outstanding_requests, req);
115✔
1166
        req->cb = cb;
115✔
1167
        req->userdata = userdata;
115✔
1168
        req->free_userdata = free_userdata;
115✔
1169
        req->is_cancelled = false;
115✔
1170
        LIST_APPEND(outstanding_requests, node->outstanding_requests, req);
115✔
1171

1172
        *ret = req;
115✔
1173
        return 0;
115✔
1174
}
1175

1176
static int agent_request_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
115✔
1177
        _cleanup_agent_request_ AgentRequest *req = userdata;
115✔
1178
        if (req->is_cancelled) {
115✔
1179
                bc_log_debugf("Response received to a cancelled request for node %s. Dropping message.",
×
1180
                              req->node->name);
1181
                return 0;
1182
        }
1183

1184
        return req->cb(req, m, ret_error);
115✔
1185
}
1186

1187
int agent_request_cancel(AgentRequest *r) {
×
1188
        _cleanup_agent_request_ AgentRequest *req = r;
×
1189
        req->is_cancelled = true;
×
1190
        _cleanup_sd_bus_message_ sd_bus_message *m = NULL;
×
1191
        sd_bus_message_new_method_errorf(req->message, &m, SD_BUS_ERROR_FAILED, "Request cancelled");
×
1192

1193
        return req->cb(req, m, NULL);
×
1194
}
1195

1196
int agent_request_start(AgentRequest *req) {
115✔
1197
        Node *node = req->node;
115✔
1198

1199
        int r = sd_bus_call_async(
115✔
1200
                        node->agent_bus,
1201
                        &req->slot,
1202
                        req->message,
1203
                        agent_request_callback,
1204
                        req,
1205
                        BC_DEFAULT_DBUS_TIMEOUT);
1206
        if (r < 0) {
115✔
1207
                return r;
1208
        }
1209

1210
        agent_request_ref(req); /* Keep alive while operation is outstanding */
115✔
1211
        return 1;
115✔
1212
}
1213

1214
AgentRequest *node_request_list_units(
4✔
1215
                Node *node, agent_request_response_t cb, void *userdata, free_func_t free_userdata) {
1216
        if (!node_has_agent(node)) {
4✔
1217
                return NULL;
4✔
1218
        }
1219

1220
        _cleanup_agent_request_ AgentRequest *req = NULL;
4✔
1221
        node_create_request(&req, node, "ListUnits", cb, userdata, free_userdata);
4✔
1222
        if (req == NULL) {
4✔
1223
                return NULL;
1224
        }
1225

1226
        if (agent_request_start(req) < 0) {
4✔
1227
                return NULL;
1228
        }
1229

1230
        return steal_pointer(&req);
4✔
1231
}
1232

1233
AgentRequest *node_request_list_unit_files(
4✔
1234
                Node *node, agent_request_response_t cb, void *userdata, free_func_t free_userdata) {
1235
        if (!node_has_agent(node)) {
4✔
1236
                return NULL;
4✔
1237
        }
1238

1239
        _cleanup_agent_request_ AgentRequest *req = NULL;
4✔
1240
        node_create_request(&req, node, "ListUnitFiles", cb, userdata, free_userdata);
4✔
1241
        if (req == NULL) {
4✔
1242
                return NULL;
1243
        }
1244

1245
        if (agent_request_start(req) < 0) {
4✔
1246
                return NULL;
1247
        }
1248

1249
        return steal_pointer(&req);
4✔
1250
}
1251

1252
/*************************************************************************
1253
 ********** org.eclipse.bluechi.Node.ListUnits **************************
1254
 ************************************************************************/
1255

1256
static int method_list_units_callback(AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
2✔
1257
        sd_bus_message *request_message = req->userdata;
2✔
1258

1259
        if (sd_bus_message_is_method_error(m, NULL)) {
2✔
1260
                /* Forward error */
1261
                return sd_bus_reply_method_error(request_message, sd_bus_message_get_error(m));
×
1262
        }
1263

1264
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
2✔
1265
        int r = sd_bus_message_new_method_return(request_message, &reply);
2✔
1266
        if (r < 0) {
2✔
1267
                return sd_bus_reply_method_errorf(
×
1268
                                request_message,
1269
                                SD_BUS_ERROR_FAILED,
1270
                                "Failed to create a reply message for ListUnits request: %s",
1271
                                strerror(-r));
1272
        }
1273

1274
        r = sd_bus_message_copy(reply, m, true);
2✔
1275
        if (r < 0) {
2✔
1276
                return sd_bus_reply_method_errorf(
×
1277
                                request_message,
1278
                                SD_BUS_ERROR_FAILED,
1279
                                "Failed to copy the bus message for ListUnits request: %s",
1280
                                strerror(-r));
1281
        }
1282

1283
        return sd_bus_message_send(reply);
2✔
1284
}
1285

1286

1287
static int node_method_list_units(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2✔
1288
        Node *node = userdata;
2✔
1289

1290
        if (node->is_shutdown) {
2✔
1291
                return sd_bus_reply_method_errorf(
×
1292
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1293
        }
1294

1295
        _cleanup_agent_request_ AgentRequest *agent_req = node_request_list_units(
2✔
1296
                        node,
1297
                        method_list_units_callback,
1298
                        sd_bus_message_ref(m),
2✔
1299
                        (free_func_t) sd_bus_message_unref);
1300
        if (agent_req == NULL) {
2✔
1301
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "List units not found");
×
1302
        }
1303

1304
        return 1;
1305
}
1306

1307
/*************************************************************************
1308
 ********** org.eclipse.bluechi.Node.ListUnitFiles ***********************
1309
 ************************************************************************/
1310

1311
static int method_list_unit_files_callback(AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
2✔
1312
        sd_bus_message *request_message = req->userdata;
2✔
1313

1314
        if (sd_bus_message_is_method_error(m, NULL)) {
2✔
1315
                /* Forward error */
1316
                return sd_bus_reply_method_error(request_message, sd_bus_message_get_error(m));
×
1317
        }
1318

1319
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
2✔
1320
        int r = sd_bus_message_new_method_return(request_message, &reply);
2✔
1321
        if (r < 0) {
2✔
1322
                return sd_bus_reply_method_errorf(
×
1323
                                request_message,
1324
                                SD_BUS_ERROR_FAILED,
1325
                                "Failed to create a reply message for ListUnitFiles request: %s",
1326
                                strerror(-r));
1327
        }
1328

1329
        r = sd_bus_message_copy(reply, m, true);
2✔
1330
        if (r < 0) {
2✔
1331
                return sd_bus_reply_method_errorf(
×
1332
                                request_message,
1333
                                SD_BUS_ERROR_FAILED,
1334
                                "Failed to copy the bus message for ListUnitFiles request: %s",
1335
                                strerror(-r));
1336
        }
1337

1338
        return sd_bus_message_send(reply);
2✔
1339
}
1340

1341
static int node_method_list_unit_files(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2✔
1342
        Node *node = userdata;
2✔
1343

1344
        if (node->is_shutdown) {
2✔
1345
                return sd_bus_reply_method_errorf(
×
1346
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1347
        }
1348

1349
        _cleanup_agent_request_ AgentRequest *agent_req = node_request_list_unit_files(
2✔
1350
                        node,
1351
                        method_list_unit_files_callback,
1352
                        sd_bus_message_ref(m),
2✔
1353
                        (free_func_t) sd_bus_message_unref);
1354
        if (agent_req == NULL) {
2✔
1355
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "List unit files not found");
×
1356
        }
1357
        return 1;
1358
}
1359

1360
/*************************************************************************
1361
 ********** org.eclipse.bluechi.Node.SetUnitProperty ******************
1362
 ************************************************************************/
1363

1364
static int node_method_set_unit_properties_callback(
1✔
1365
                AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
1366
        sd_bus_message *request_message = req->userdata;
1✔
1367

1368
        if (sd_bus_message_is_method_error(m, NULL)) {
1✔
1369
                /* Forward error */
1370
                return sd_bus_reply_method_error(request_message, sd_bus_message_get_error(m));
×
1371
        }
1372

1373
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
1✔
1374
        int r = sd_bus_message_new_method_return(request_message, &reply);
1✔
1375
        if (r < 0) {
1✔
1376
                return sd_bus_reply_method_errorf(
×
1377
                                request_message,
1378
                                SD_BUS_ERROR_FAILED,
1379
                                "Failed to create a reply message: %s",
1380
                                strerror(-r));
1381
        }
1382

1383
        return sd_bus_message_send(reply);
1✔
1384
}
1385

1386
static int node_method_set_unit_properties(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1387
        Node *node = userdata;
1✔
1388
        const char *unit = NULL;
1✔
1389
        int runtime = 0;
1✔
1390

1391
        if (node->is_shutdown) {
1✔
1392
                return sd_bus_reply_method_errorf(
×
1393
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1394
        }
1395

1396
        int r = sd_bus_message_read(m, "sb", &unit, &runtime);
1✔
1397
        if (r < 0) {
1✔
1398
                return sd_bus_reply_method_errorf(
×
1399
                                m,
1400
                                SD_BUS_ERROR_INVALID_ARGS,
1401
                                "Invalid argument for unit or runtime: %s",
1402
                                strerror(-r));
1403
        }
1404

1405
        _cleanup_agent_request_ AgentRequest *req = NULL;
1✔
1406
        r = node_create_request(
1✔
1407
                        &req,
1408
                        node,
1409
                        "SetUnitProperties",
1410
                        node_method_set_unit_properties_callback,
1411
                        sd_bus_message_ref(m),
1✔
1412
                        (free_func_t) sd_bus_message_unref);
1413
        if (req == NULL) {
1✔
1414
                sd_bus_message_unref(m);
×
1415

1416
                return sd_bus_reply_method_errorf(
×
1417
                                m, SD_BUS_ERROR_FAILED, "Failed to create an agent request: %s", strerror(-r));
1418
        }
1419

1420
        r = sd_bus_message_append(req->message, "sb", unit, runtime);
1✔
1421
        if (r < 0) {
1✔
1422
                return sd_bus_reply_method_errorf(
×
1423
                                m,
1424
                                SD_BUS_ERROR_FAILED,
1425
                                "Failed to append unit and runtime to the message: %s",
1426
                                strerror(-r));
1427
        }
1428

1429
        r = sd_bus_message_copy(req->message, m, false);
1✔
1430
        if (r < 0) {
1✔
1431
                return sd_bus_reply_method_errorf(
×
1432
                                m, SD_BUS_ERROR_FAILED, "Failed to copy a message: %s", strerror(-r));
1433
        }
1434

1435
        r = agent_request_start(req);
1✔
1436
        if (r < 0) {
1✔
1437
                return sd_bus_reply_method_errorf(
×
1438
                                m,
1439
                                SD_BUS_ERROR_FAILED,
1440
                                "Failed to call the method to start the node: %s",
1441
                                strerror(-r));
1442
        }
1443

1444
        return 1;
1445
}
1446

1447
static int node_method_passthrough_to_agent_callback(
38✔
1448
                AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
1449
        sd_bus_message *request_message = req->userdata;
38✔
1450

1451
        if (sd_bus_message_is_method_error(m, NULL)) {
38✔
1452
                return sd_bus_reply_method_error(request_message, sd_bus_message_get_error(m));
2✔
1453
        }
1454

1455
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
38✔
1456
        int r = sd_bus_message_new_method_return(request_message, &reply);
36✔
1457
        if (r < 0) {
36✔
1458
                return sd_bus_reply_method_errorf(
×
1459
                                request_message,
1460
                                SD_BUS_ERROR_FAILED,
1461
                                "Failed to create a reply message: %s",
1462
                                strerror(-r));
1463
        }
1464

1465
        r = sd_bus_message_copy(reply, m, true);
36✔
1466
        if (r < 0) {
36✔
1467
                return sd_bus_reply_method_errorf(
×
1468
                                request_message,
1469
                                SD_BUS_ERROR_FAILED,
1470
                                "Failed to copy a reply message: %s",
1471
                                strerror(-r));
1472
        }
1473

1474
        return sd_bus_message_send(reply);
36✔
1475
}
1476

1477
static int node_method_passthrough_to_agent(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
38✔
1478
        Node *node = userdata;
38✔
1479

1480
        if (node->is_shutdown) {
38✔
1481
                return sd_bus_reply_method_errorf(
×
1482
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1483
        }
1484

1485
        _cleanup_agent_request_ AgentRequest *req = NULL;
38✔
1486
        int r = node_create_request(
38✔
1487
                        &req,
1488
                        node,
1489
                        sd_bus_message_get_member(m),
1490
                        node_method_passthrough_to_agent_callback,
1491
                        sd_bus_message_ref(m),
38✔
1492
                        (free_func_t) sd_bus_message_unref);
1493
        if (req == NULL) {
38✔
1494
                sd_bus_message_unref(m);
×
1495

1496
                return sd_bus_reply_method_errorf(
×
1497
                                m, SD_BUS_ERROR_FAILED, "Failed to create an agent request: %s", strerror(-r));
1498
        }
1499

1500
        r = sd_bus_message_copy(req->message, m, true);
38✔
1501
        if (r < 0) {
38✔
1502
                return sd_bus_reply_method_errorf(
×
1503
                                m, SD_BUS_ERROR_FAILED, "Failed to copy a reply message: %s", strerror(-r));
1504
        }
1505

1506
        r = agent_request_start(req);
38✔
1507
        if (r < 0) {
38✔
1508
                return sd_bus_reply_method_errorf(
×
1509
                                m,
1510
                                SD_BUS_ERROR_FAILED,
1511
                                "Failed to call the method to start the node: %s",
1512
                                strerror(-r));
1513
        }
1514

1515
        return 1;
1516
}
1517

1518
/* Keep track of data related to setting up a job. For example calling
1519
   the initial agent request before we know the job is actually going to
1520
   happen. */
1521
typedef struct {
1522
        int ref_count;
1523
        sd_bus_message *request_message;
1524
        Job *job;
1525
} JobSetup;
1526

1527
static JobSetup *job_setup_ref(JobSetup *setup) {
67✔
1528
        setup->ref_count++;
67✔
1529
        return setup;
67✔
1530
}
1531

1532
static void job_setup_unref(JobSetup *setup) {
134✔
1533
        setup->ref_count--;
134✔
1534
        if (setup->ref_count != 0) {
134✔
1535
                return;
1536
        }
1537

1538
        job_unrefp(&setup->job);
67✔
1539
        sd_bus_message_unrefp(&setup->request_message);
67✔
1540
        free(setup);
67✔
1541
}
1542

1543
DEFINE_CLEANUP_FUNC(JobSetup, job_setup_unref)
67✔
1544
#define _cleanup_job_setup_ _cleanup_(job_setup_unrefp)
1545

1546
static JobSetup *job_setup_new(sd_bus_message *request_message, Node *node, const char *unit, const char *type) {
67✔
1547
        _cleanup_job_setup_ JobSetup *setup = malloc0(sizeof(JobSetup));
67✔
1548
        if (setup == NULL) {
67✔
1549
                return NULL;
1550
        }
1551

1552
        setup->ref_count = 1;
67✔
1553
        setup->request_message = sd_bus_message_ref(request_message);
67✔
1554
        setup->job = job_new(node, unit, type);
67✔
1555
        if (setup->job == NULL) {
67✔
1556
                NULL;
67✔
1557
        }
1558

1559
        return steal_pointer(&setup);
67✔
1560
}
1561

1562
static int unit_lifecycle_method_callback(AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
67✔
1563
        Node *node = req->node;
67✔
1564
        Controller *controller = node->controller;
67✔
1565
        JobSetup *setup = req->userdata;
67✔
1566

1567
        if (sd_bus_message_is_method_error(m, NULL)) {
67✔
1568
                /* Forward error */
1569
                return sd_bus_reply_method_error(setup->request_message, sd_bus_message_get_error(m));
1✔
1570
        }
1571

1572
        if (!controller_add_job(controller, setup->job)) {
66✔
1573
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to add a job");
×
1574
        }
1575

1576
        return sd_bus_reply_method_return(setup->request_message, "o", setup->job->object_path);
66✔
1577
}
1578

1579
static int node_run_unit_lifecycle_method(
67✔
1580
                sd_bus_message *m, Node *node, const char *job_type, const char *method) {
1581
        const char *unit = NULL;
67✔
1582
        const char *mode = NULL;
67✔
1583
        uint64_t start_time = get_time_micros();
67✔
1584

1585
        if (node->is_shutdown) {
67✔
1586
                return sd_bus_reply_method_errorf(
×
1587
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1588
        }
1589

1590
        int r = sd_bus_message_read(m, "ss", &unit, &mode);
67✔
1591
        if (r < 0) {
67✔
1592
                return sd_bus_reply_method_errorf(
×
1593
                                m,
1594
                                SD_BUS_ERROR_INVALID_ARGS,
1595
                                "Invalid argument for unit or mode: %s",
1596
                                strerror(-r));
1597
        }
1598

1599
        _cleanup_job_setup_ JobSetup *setup = job_setup_new(m, node, unit, job_type);
134✔
1600
        if (setup == NULL) {
67✔
1601
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "Out of memory");
×
1602
        }
1603

1604
        if (node->controller->metrics_enabled) {
67✔
1605
                setup->job->job_start_micros = start_time;
5✔
1606
        }
1607

1608
        _cleanup_agent_request_ AgentRequest *req = NULL;
67✔
1609
        r = node_create_request(
67✔
1610
                        &req,
1611
                        node,
1612
                        method,
1613
                        unit_lifecycle_method_callback,
1614
                        job_setup_ref(setup),
67✔
1615
                        (free_func_t) job_setup_unref);
1616
        if (req == NULL) {
67✔
1617
                job_setup_unref(setup);
×
1618

1619
                return sd_bus_reply_method_errorf(
×
1620
                                m, SD_BUS_ERROR_FAILED, "Failed to create an agent request: %s", strerror(-r));
1621
        }
1622

1623
        r = sd_bus_message_append(req->message, "ssu", unit, mode, setup->job->id);
67✔
1624
        if (r < 0) {
67✔
1625
                return sd_bus_reply_method_errorf(
×
1626
                                m,
1627
                                SD_BUS_ERROR_FAILED,
1628
                                "Failed to append unit, mode, and job ID to the message: %s",
1629
                                strerror(-r));
1630
        }
1631

1632
        r = agent_request_start(req);
67✔
1633
        if (r < 0) {
67✔
1634
                return sd_bus_reply_method_errorf(
×
1635
                                m,
1636
                                SD_BUS_ERROR_FAILED,
1637
                                "Failed to call the method to start the node: %s",
1638
                                strerror(-r));
1639
        }
1640

1641
        return 1;
1642
}
1643

1644

1645
/*************************************************************************
1646
 ********** org.eclipse.bluechi.Node.StartUnit **************************
1647
 ************************************************************************/
1648

1649
static int node_method_start_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
48✔
1650
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "start", "StartUnit");
48✔
1651
}
1652

1653
/*************************************************************************
1654
 ********** org.eclipse.bluechi.Node.StopUnit ***************************
1655
 ************************************************************************/
1656

1657

1658
static int node_method_stop_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
16✔
1659
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "stop", "StopUnit");
16✔
1660
}
1661

1662
/*************************************************************************
1663
 ********** org.eclipse.bluechi.Node.RestartUnit ************************
1664
 ************************************************************************/
1665

1666
static int node_method_restart_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2✔
1667
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "restart", "RestartUnit");
2✔
1668
}
1669

1670
/*************************************************************************
1671
 ********** org.eclipse.bluechi.Node.ReloadUnit **************************
1672
 ************************************************************************/
1673

1674
static int node_method_reload_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1675
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "reload", "ReloadUnit");
1✔
1676
}
1677

1678
/*************************************************************************
1679
 ********** org.eclipse.bluechi.Node.SetLogLevel *******************
1680
 ************************************************************************/
1681

1682
static int node_method_set_log_level(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) {
3✔
1683
        const char *level = NULL;
3✔
1684
        Node *node = (Node *) userdata;
3✔
1685
        sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
1686
        _cleanup_sd_bus_message_ sd_bus_message *sub_m = NULL;
3✔
1687

1688
        int r = sd_bus_message_read(m, "s", &level);
3✔
1689
        if (r < 0) {
3✔
1690
                return sd_bus_reply_method_errorf(
×
1691
                                m,
1692
                                SD_BUS_ERROR_INVALID_ARGS,
1693
                                "Invalid argument for the log-level: %s",
1694
                                strerror(-r));
1695
        }
1696
        LogLevel loglevel = string_to_log_level(level);
3✔
1697
        if (loglevel == LOG_LEVEL_INVALID) {
3✔
1698
                r = sd_bus_reply_method_return(m, "");
1✔
1699
                if (r < 0) {
1✔
1700
                        return sd_bus_reply_method_errorf(
×
1701
                                        m,
1702
                                        SD_BUS_ERROR_INVALID_ARGS,
1703
                                        "Invalid argument for the log level invalid");
1704
                }
1705
        }
1706
        r = sd_bus_call_method(
3✔
1707
                        node->agent_bus,
1708
                        BC_AGENT_DBUS_NAME,
1709
                        INTERNAL_AGENT_OBJECT_PATH,
1710
                        INTERNAL_AGENT_INTERFACE,
1711
                        "SetLogLevel",
1712
                        &error,
1713
                        &sub_m,
1714
                        "s",
1715
                        level);
1716
        if (r < 0) {
3✔
1717
                bc_log_errorf("Failed to set log level call: %s", error.message);
1✔
1718
                sd_bus_error_free(&error);
1✔
1719
                return sd_bus_reply_method_errorf(
1✔
1720
                                m,
1721
                                SD_BUS_ERROR_FAILED,
1722
                                "Failed to call a method to set the log level: %s",
1723
                                strerror(-r));
1724
        }
1725
        return sd_bus_reply_method_return(m, "");
2✔
1726
}
1727

1728
static int send_agent_simple_message(Node *node, const char *method, const char *arg) {
86✔
1729
        _cleanup_sd_bus_message_ sd_bus_message *m = NULL;
86✔
1730
        int r = sd_bus_message_new_method_call(
86✔
1731
                        node->agent_bus,
1732
                        &m,
1733
                        BC_AGENT_DBUS_NAME,
1734
                        INTERNAL_AGENT_OBJECT_PATH,
1735
                        INTERNAL_AGENT_INTERFACE,
1736
                        method);
1737
        if (r < 0) {
86✔
1738
                return r;
1739
        }
1740

1741
        if (arg != NULL) {
86✔
1742
                r = sd_bus_message_append(m, "s", arg);
82✔
1743
                if (r < 0) {
82✔
1744
                        return r;
1745
                }
1746
        }
1747

1748
        return sd_bus_send(node->agent_bus, m, NULL);
86✔
1749
}
1750

1751
static void node_send_agent_subscribe(Node *node, const char *unit) {
30✔
1752
        if (!node_has_agent(node)) {
30✔
1753
                return;
1754
        }
1755

1756
        int r = send_agent_simple_message(node, "Subscribe", unit);
30✔
1757
        if (r < 0) {
30✔
1758
                bc_log_error("Failed to subscribe w/ agent");
×
1759
        }
1760
}
1761

1762

1763
static void node_send_agent_unsubscribe(Node *node, const char *unit) {
29✔
1764
        if (!node_has_agent(node)) {
29✔
1765
                return;
1766
        }
1767

1768
        int r = send_agent_simple_message(node, "Unsubscribe", unit);
29✔
1769
        if (r < 0) {
29✔
1770
                bc_log_error("Failed to unsubscribe w/ agent");
×
1771
        }
1772
}
1773

1774
/* Resubscribe to all subscriptions */
1775
static void node_send_agent_subscribe_all(Node *node) {
304✔
1776
        void *item = NULL;
304✔
1777
        size_t i = 0;
304✔
1778

1779
        while (hashmap_iter(node->unit_subscriptions, &i, &item)) {
305✔
1780
                UnitSubscriptions *usubs = item;
1✔
1781
                node_send_agent_subscribe(node, usubs->unit);
1✔
1782
        }
1783
}
304✔
1784

1785
void node_subscribe(Node *node, Subscription *sub) {
29✔
1786
        SubscribedUnit *sub_unit = NULL;
29✔
1787
        SubscribedUnit *next_sub_unit = NULL;
29✔
1788
        LIST_FOREACH_SAFE(units, sub_unit, next_sub_unit, sub->subscribed_units) {
59✔
1789
                const UnitSubscriptionsKey key = { sub_unit->name };
30✔
1790
                UnitSubscriptions *usubs = NULL;
30✔
1791

1792
                _cleanup_free_ UnitSubscription *usub = malloc0(sizeof(UnitSubscription));
30✔
1793
                if (usub == NULL) {
30✔
1794
                        bc_log_error("Failed to subscribe to unit, OOM");
×
1795
                        return;
1796
                }
1797
                usub->sub = sub;
30✔
1798

1799
                usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
30✔
1800
                if (usubs == NULL) {
30✔
1801
                        UnitSubscriptions v = { NULL, NULL, false, _UNIT_ACTIVE_STATE_INVALID, NULL };
29✔
1802
                        v.unit = strdup(key.unit);
29✔
1803
                        if (v.unit == NULL) {
29✔
1804
                                bc_log_error("Failed to subscribe to unit, OOM");
×
1805
                                return;
×
1806
                        }
1807

1808
                        usubs = (UnitSubscriptions *) hashmap_set(node->unit_subscriptions, &v);
29✔
1809
                        if (usubs == NULL && hashmap_oom(node->unit_subscriptions)) {
29✔
1810
                                free(v.unit);
×
1811
                                bc_log_error("Failed to subscribe to unit, OOM");
×
1812
                                return;
1813
                        }
1814

1815
                        /* First sub to this unit, pass to agent */
1816
                        node_send_agent_subscribe(node, sub_unit->name);
29✔
1817

1818
                        usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
29✔
1819
                }
1820

1821
                LIST_APPEND(subs, usubs->subs, steal_pointer(&usub));
30✔
1822

1823
                /* We know this is loaded, so we won't get notified from
1824
                   the agent, instead send a virtual event here. */
1825
                if (usubs->loaded) {
30✔
1826
                        int r = sub->handle_unit_new(sub->monitor, node->name, sub_unit->name, "virtual");
1✔
1827
                        if (r < 0) {
1✔
1828
                                bc_log_error("Failed to emit UnitNew signal");
×
1829
                        }
1830

1831
                        if (usubs->active_state >= 0) {
1✔
1832
                                r = sub->handle_unit_state_changed(
1✔
1833
                                                sub->monitor,
1834
                                                node->name,
1✔
1835
                                                sub_unit->name,
1✔
1836
                                                active_state_to_string(usubs->active_state),
1837
                                                usubs->substate ? usubs->substate : "invalid",
1✔
1838
                                                "virtual");
1839
                                if (r < 0) {
1✔
1840
                                        bc_log_error("Failed to emit UnitNew signal");
×
1841
                                }
1842
                        }
1843
                }
1844
        }
1845
}
1846

1847
void node_unsubscribe(Node *node, Subscription *sub) {
29✔
1848
        SubscribedUnit *sub_unit = NULL;
29✔
1849
        SubscribedUnit *next_sub_unit = NULL;
29✔
1850
        LIST_FOREACH_SAFE(units, sub_unit, next_sub_unit, sub->subscribed_units) {
59✔
1851
                UnitSubscriptionsKey key = { sub_unit->name };
30✔
1852
                UnitSubscriptions *usubs = NULL;
30✔
1853
                UnitSubscription *usub = NULL;
30✔
1854
                UnitSubscription *found = NULL;
30✔
1855
                UnitSubscriptions *deleted = NULL;
30✔
1856

1857
                /* NOTE: If there are errors during subscribe we may still
1858
                   call unsubscribe, so this must silently handle the
1859
                   case of too many unsubscribes. */
1860

1861
                usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
30✔
1862
                if (usubs == NULL) {
30✔
1863
                        continue;
×
1864
                }
1865

1866
                LIST_FOREACH(subs, usub, usubs->subs) {
30✔
1867
                        if (usub->sub == sub) {
30✔
1868
                                found = usub;
1869
                                break;
1870
                        }
1871
                }
1872

1873
                if (found == NULL) {
30✔
1874
                        continue;
×
1875
                }
1876

1877
                LIST_REMOVE(subs, usubs->subs, found);
30✔
1878
                free_and_null(found);
30✔
1879

1880
                if (LIST_IS_EMPTY(usubs->subs)) {
30✔
1881
                        /* Last subscription for this unit, tell agent */
1882
                        node_send_agent_unsubscribe(node, sub_unit->name);
29✔
1883
                        deleted = (UnitSubscriptions *) hashmap_delete(node->unit_subscriptions, &key);
29✔
1884
                        if (deleted) {
29✔
1885
                                unit_subscriptions_clear(deleted);
29✔
1886
                        }
1887
                }
1888
        }
1889
}
29✔
1890

1891
static void node_start_proxy_dependency(Node *node, ProxyDependency *dep) {
12✔
1892
        if (!node_has_agent(node)) {
12✔
1893
                return;
1894
        }
1895

1896
        bc_log_infof("Starting dependency %s on node %s", dep->unit_name, node->name);
12✔
1897

1898
        int r = send_agent_simple_message(node, "StartDep", dep->unit_name);
12✔
1899
        if (r < 0) {
12✔
1900
                bc_log_error("Failed to send StartDep to agent");
×
1901
        }
1902
}
1903

1904
static void node_start_proxy_dependency_all(Node *node) {
304✔
1905
        ProxyDependency *dep = NULL;
304✔
1906
        LIST_FOREACH(deps, dep, node->proxy_dependencies) {
304✔
1907
                node_start_proxy_dependency(node, dep);
×
1908
        }
1909
}
304✔
1910

1911
static void node_stop_proxy_dependency(Node *node, ProxyDependency *dep) {
11✔
1912
        if (!node_has_agent(node)) {
11✔
1913
                return;
1914
        }
1915

1916
        bc_log_infof("Stopping dependency %s on node %s", dep->unit_name, node->name);
11✔
1917

1918
        int r = send_agent_simple_message(node, "StopDep", dep->unit_name);
11✔
1919
        if (r < 0) {
11✔
1920
                bc_log_error("Failed to send StopDep to agent");
×
1921
        }
1922
}
1923

1924
static struct ProxyDependency *node_find_proxy_dependency(Node *node, const char *unit_name) {
24✔
1925
        ProxyDependency *dep = NULL;
24✔
1926
        LIST_FOREACH(deps, dep, node->proxy_dependencies) {
24✔
1927
                if (streq(dep->unit_name, unit_name)) {
13✔
1928
                        return dep;
1929
                }
1930
        }
1931

1932
        return NULL;
1933
}
1934

1935
int node_add_proxy_dependency(Node *node, const char *unit_name) {
12✔
1936
        ProxyDependency *dep = NULL;
12✔
1937

1938
        dep = node_find_proxy_dependency(node, unit_name);
12✔
1939
        if (dep) {
12✔
1940
                dep->n_deps++;
1✔
1941
                /* Always start, if the dep service was stopped by
1942
                   the target service stopping */
1943
                node_start_proxy_dependency(node, dep);
1✔
1944
                return 0;
1✔
1945
        }
1946

1947
        _cleanup_free_ char *unit_name_copy = strdup(unit_name);
23✔
1948
        if (unit_name_copy == NULL) {
11✔
1949
                return -ENOMEM;
1950
        }
1951

1952
        dep = malloc0(sizeof(ProxyDependency));
11✔
1953
        if (dep == NULL) {
11✔
1954
                return -ENOMEM;
1955
        }
1956

1957
        dep->unit_name = steal_pointer(&unit_name_copy);
11✔
1958
        dep->n_deps = 1;
11✔
1959
        LIST_APPEND(deps, node->proxy_dependencies, dep);
11✔
1960

1961
        node_start_proxy_dependency(node, dep);
11✔
1962

1963
        return 0;
1964
}
1965

1966
int node_remove_proxy_dependency(Node *node, const char *unit_name) {
12✔
1967
        ProxyDependency *dep = NULL;
12✔
1968
        dep = node_find_proxy_dependency(node, unit_name);
12✔
1969
        if (!dep) {
12✔
1970
                return -ENOENT;
1971
        }
1972

1973
        dep->n_deps--;
12✔
1974

1975
        if (dep->n_deps == 0) {
12✔
1976
                /* Only stop on the last dep */
1977
                node_stop_proxy_dependency(node, dep);
11✔
1978

1979
                LIST_REMOVE(deps, node->proxy_dependencies, dep);
11✔
1980
                proxy_dependency_free(dep);
11✔
1981
        }
1982

1983
        return 0;
1984
}
1985

1986
int node_method_get_unit_uint64_property_sync(Node *node, char *unit, char *property, uint64_t *value) {
6✔
1987
        int r = 0;
6✔
1988
        _cleanup_sd_bus_message_ sd_bus_message *message = NULL;
6✔
1989
        sd_bus_error error = SD_BUS_ERROR_NULL;
6✔
1990
        r = sd_bus_call_method(
6✔
1991
                        node->agent_bus,
1992
                        BC_AGENT_DBUS_NAME,
1993
                        INTERNAL_AGENT_OBJECT_PATH,
1994
                        INTERNAL_AGENT_INTERFACE,
1995
                        "GetUnitProperty",
1996
                        &error,
1997
                        &message,
1998
                        "sss",
1999
                        unit,
2000
                        "org.freedesktop.systemd1.Unit",
2001
                        property);
2002
        if (r < 0) {
6✔
2003
                bc_log_errorf("Failed to issue GetUnitProperty call: %s", error.message);
×
2004
                sd_bus_error_free(&error);
×
2005
                return r;
2006
        }
2007

2008
        r = sd_bus_message_enter_container(message, SD_BUS_TYPE_VARIANT, "t");
6✔
2009
        if (r < 0) {
6✔
2010
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2011
                return r;
2012
        }
2013

2014
        r = sd_bus_message_read_basic(message, SD_BUS_TYPE_UINT64, value);
6✔
2015
        if (r < 0) {
6✔
2016
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2017
                return r;
2018
        }
2019

2020
        r = sd_bus_message_exit_container(message);
6✔
2021
        if (r < 0) {
6✔
2022
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2023
                return r;
2024
        }
2025

2026
        return 0;
2027
}
2028

2029
void node_enable_metrics(Node *node) {
3✔
2030
        if (!node_has_agent(node)) {
3✔
2031
                return;
2032
        }
2033

2034
        int r = send_agent_simple_message(node, "EnableMetrics", NULL);
3✔
2035
        if (r < 0) {
3✔
2036
                bc_log_error("Failed to enable metrics on agent");
×
2037
        }
2038

2039
        if (!metrics_node_signal_matching_register(node)) {
3✔
2040
                bc_log_error("Failed to enable metrics on agent");
×
2041
        }
2042
}
2043

2044
void node_disable_metrics(Node *node) {
1✔
2045
        if (!node_has_agent(node)) {
1✔
2046
                return;
2047
        }
2048

2049
        int r = send_agent_simple_message(node, "DisableMetrics", NULL);
1✔
2050
        if (r < 0) {
1✔
2051
                bc_log_error("Failed to disable metrics on agent");
×
2052
        }
2053

2054
        sd_bus_slot_unrefp(&node->metrics_matching_slot);
1✔
2055
        node->metrics_matching_slot = NULL;
1✔
2056
}
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