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

eclipse-bluechi / bluechi / 19941333032

04 Dec 2025 07:27PM UTC coverage: 82.516% (-0.1%) from 82.632%
19941333032

push

github

engelmi
Remove test data with too long log configs

The test cases for too long log configs (level, target and isquiet) causes
the bluechi-agent service to fail (as expected). However, this happens in
the test setup phase where we expect a running agent. Due to timing issues
this check might even pass, depending how fast the status is being checked.
This causes the test to be flacky.
Since such a long input for log configs is unrealistic and the proper level
for testing is on the unit level (test without the long data is already in
place), these cases have een removed.
In addition, the unused BluechiConfig variable for max line length has been
removed.

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

5687 of 6892 relevant lines covered (82.52%)

1865.63 hits per line

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

83.91
/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) {
9✔
109
        free_and_null(dep->unit_name);
9✔
110
        free_and_null(dep);
9✔
111
}
9✔
112

113
struct ProxyTarget {
114
        char *target_name;
115
        LIST_FIELDS(ProxyTarget, allowed_targets);
116
};
117

118
static void proxy_target_free(struct ProxyTarget *target) {
10✔
119
        free_and_null(target->target_name);
10✔
120
        free_and_null(target);
10✔
121
}
10✔
122

123
typedef struct UnitSubscription UnitSubscription;
124

125
struct UnitSubscription {
126
        Subscription *sub;
127
        LIST_FIELDS(UnitSubscription, subs);
128
};
129

130
typedef struct {
131
        char *unit;
132
        LIST_HEAD(UnitSubscription, subs);
133
        bool loaded;
134
        UnitActiveState active_state;
135
        char *substate;
136
} UnitSubscriptions;
137

138
typedef struct {
139
        char *unit;
140
} UnitSubscriptionsKey;
141

142
static void unit_subscriptions_clear(void *item) {
27✔
143
        UnitSubscriptions *usubs = item;
27✔
144
        free_and_null(usubs->unit);
27✔
145
        free_and_null(usubs->substate);
27✔
146
        assert(LIST_IS_EMPTY(usubs->subs));
27✔
147
}
27✔
148

149
static uint64_t unit_subscriptions_hash(const void *item, uint64_t seed0, uint64_t seed1) {
990✔
150
        const UnitSubscriptions *usubs = item;
990✔
151
        return hashmap_sip(usubs->unit, strlen(usubs->unit), seed0, seed1);
990✔
152
}
153

154
static int unit_subscriptions_compare(const void *a, const void *b, UNUSED void *udata) {
554✔
155
        const UnitSubscriptions *usubs_a = a;
554✔
156
        const UnitSubscriptions *usubs_b = b;
554✔
157

158
        return strcmp(usubs_a->unit, usubs_b->unit);
554✔
159
}
160

161

162
Node *node_new(Controller *controller, const char *name) {
601✔
163
        _cleanup_node_ Node *node = malloc0(sizeof(Node));
601✔
164
        if (node == NULL) {
601✔
165
                return NULL;
166
        }
167

168
        node->ref_count = 1;
601✔
169
        node->controller = controller;
601✔
170
        LIST_INIT(nodes, node);
601✔
171
        LIST_HEAD_INIT(node->outstanding_requests);
601✔
172
        LIST_HEAD_INIT(node->proxy_monitors);
601✔
173
        LIST_HEAD_INIT(node->proxy_dependencies);
601✔
174
        LIST_HEAD_INIT(node->allowed_proxy_targets);
601✔
175

176
        node->unit_subscriptions = hashmap_new(
601✔
177
                        sizeof(UnitSubscriptions),
178
                        0,
179
                        0,
180
                        0,
181
                        unit_subscriptions_hash,
182
                        unit_subscriptions_compare,
183
                        unit_subscriptions_clear,
184
                        NULL);
185
        if (node->unit_subscriptions == NULL) {
601✔
186
                return NULL;
187
        }
188

189
        node->last_seen = 0;
601✔
190
        node->last_seen_monotonic = 0;
601✔
191

192
        node->name = NULL;
601✔
193
        if (name) {
601✔
194
                node->name = strdup(name);
368✔
195
                if (node->name == NULL) {
368✔
196
                        return NULL;
197
                }
198

199
                int r = assemble_object_path_string(NODE_OBJECT_PATH_PREFIX, name, &node->object_path);
368✔
200
                if (r < 0) {
368✔
201
                        return NULL;
202
                }
203
        }
204
        node->peer_ip = NULL;
601✔
205
        node->peer_selinux_context = NULL;
601✔
206

207
        node->is_shutdown = false;
601✔
208

209
        return steal_pointer(&node);
601✔
210
}
211

212
Node *node_ref(Node *node) {
132✔
213
        node->ref_count++;
132✔
214
        return node;
132✔
215
}
216

217
void node_unref(Node *node) {
733✔
218
        node->ref_count--;
733✔
219
        if (node->ref_count != 0) {
733✔
220
                return;
221
        }
222

223
        ProxyMonitor *proxy_monitor = NULL;
601✔
224
        ProxyMonitor *next_proxy_monitor = NULL;
601✔
225
        LIST_FOREACH_SAFE(monitors, proxy_monitor, next_proxy_monitor, node->proxy_monitors) {
601✔
226
                node_remove_proxy_monitor(node, proxy_monitor);
×
227
        }
228

229

230
        ProxyDependency *dep = NULL;
601✔
231
        ProxyDependency *next_dep = NULL;
601✔
232
        LIST_FOREACH_SAFE(deps, dep, next_dep, node->proxy_dependencies) {
601✔
233
                proxy_dependency_free(dep);
×
234
        }
235

236
        ProxyTarget *target = NULL;
601✔
237
        ProxyTarget *next_target = NULL;
601✔
238
        LIST_FOREACH_SAFE(allowed_targets, target, next_target, node->allowed_proxy_targets) {
611✔
239
                proxy_target_free(target);
10✔
240
        }
241

242
        node_unset_agent_bus(node);
601✔
243
        sd_bus_slot_unrefp(&node->export_slot);
601✔
244

245
        hashmap_free(node->unit_subscriptions);
601✔
246

247
        free_and_null(node->name);
601✔
248
        free_and_null(node->object_path);
601✔
249
        free_and_null(node->peer_ip);
601✔
250
        free_and_null(node->required_selinux_context);
601✔
251
        free(node);
601✔
252
}
253

254
void node_shutdown(Node *node) {
25✔
255
        AgentRequest *req = NULL;
25✔
256
        AgentRequest *next_req = NULL;
25✔
257
        node->is_shutdown = true;
25✔
258
        LIST_FOREACH_SAFE(outstanding_requests, req, next_req, node->outstanding_requests) {
25✔
259
                agent_request_cancel(req);
×
260
        }
261
}
25✔
262

263
bool node_set_required_selinux_context(Node *node, const char *selinux_context) {
×
264
        node->required_selinux_context = strdup(selinux_context);
×
265
        if (node->required_selinux_context == NULL) {
×
266
                return false;
×
267
        }
268
        return true;
269
}
270

271
int node_add_allowed_proxy_target(Node *node, const char *target_name) {
10✔
272
        ProxyTarget *target = NULL;
10✔
273

274
        _cleanup_free_ char *target_name_copy = strdup(target_name);
20✔
275
        if (target_name_copy == NULL) {
10✔
276
                return -ENOMEM;
277
        }
278

279
        target = malloc0(sizeof(ProxyTarget));
10✔
280
        if (target == NULL) {
10✔
281
                return -ENOMEM;
282
        }
283

284
        target->target_name = steal_pointer(&target_name_copy);
10✔
285
        LIST_APPEND(allowed_targets, node->allowed_proxy_targets, target);
10✔
286

287
        return 0;
288
}
289

290
static ProxyTarget *node_find_allowed_proxy_target(Node *node, const char *name) {
13✔
291
        ProxyTarget *curr = NULL;
13✔
292
        ProxyTarget *next = NULL;
13✔
293
        LIST_FOREACH_SAFE(allowed_targets, curr, next, node->allowed_proxy_targets) {
13✔
294
                if (streq(curr->target_name, name)) {
10✔
295
                        return curr;
296
                }
297
        }
298
        return NULL;
299
}
300

301
bool node_export(Node *node) {
365✔
302
        Controller *controller = node->controller;
365✔
303

304
        assert(node->name != NULL);
365✔
305

306
        int r = sd_bus_add_object_vtable(
730✔
307
                        controller->api_bus,
308
                        &node->export_slot,
309
                        node->object_path,
365✔
310
                        NODE_INTERFACE,
311
                        node_vtable,
312
                        node);
313
        if (r < 0) {
365✔
314
                bc_log_errorf("Failed to add node vtable: %s", strerror(-r));
×
315
                return false;
×
316
        }
317

318
        return true;
319
}
320

321
static int debug_messages_handler(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
322
        Node *node = userdata;
323
        if (node->name) {
324
                bc_log_infof("Incoming message from node '%s' (fd %d): path: %s, iface: %s, member: %s, signature: '%s'",
325
                             node->name,
326
                             sd_bus_get_fd(node->agent_bus),
327
                             sd_bus_message_get_path(m),
328
                             sd_bus_message_get_interface(m),
329
                             sd_bus_message_get_member(m),
330
                             sd_bus_message_get_signature(m, true));
331
        } else {
332
                bc_log_infof("Incoming message from node fd %d: path: %s, iface: %s, member: %s, signature: '%s'",
333
                             sd_bus_get_fd(node->agent_bus),
334
                             sd_bus_message_get_path(m),
335
                             sd_bus_message_get_interface(m),
336
                             sd_bus_message_get_member(m),
337
                             sd_bus_message_get_signature(m, true));
338
        }
339
        return 0;
340
}
341

342
bool node_has_agent(Node *node) {
1,904✔
343
        return node->agent_bus != NULL;
1,904✔
344
}
345

346
bool node_is_online(Node *node) {
607✔
347
        return node && node->name && node_has_agent(node);
607✔
348
}
349

350

351
static uint64_t subscription_hashmap_hash(const void *item, UNUSED uint64_t seed0, UNUSED uint64_t seed1) {
379✔
352
        const Subscription * const *subscriptionp = item;
379✔
353
        return (uint64_t) ((uintptr_t) *subscriptionp);
379✔
354
}
355

356
static int subscription_hashmap_compare(const void *a, const void *b, UNUSED void *udata) {
×
357
        const Subscription * const *subscription_a_p = a;
×
358
        const Subscription * const *subscription_b_p = b;
×
359
        if ((*subscription_a_p)->monitor == (*subscription_b_p)->monitor) {
×
360
                return 0;
×
361
        }
362
        return 1;
363
}
364

365
static struct hashmap *node_compute_unique_monitor_subscriptions(Node *node, const char *unit) {
380✔
366
        struct hashmap *unique_subs = hashmap_new(
380✔
367
                        sizeof(void *), 0, 0, 0, subscription_hashmap_hash, subscription_hashmap_compare, NULL, NULL);
368
        if (unique_subs == NULL) {
380✔
369
                return NULL;
380✔
370
        }
371

372
        const UnitSubscriptionsKey key = { (char *) unit };
380✔
373
        const UnitSubscriptions *usubs = hashmap_get(node->unit_subscriptions, &key);
380✔
374
        if (usubs != NULL) {
380✔
375
                UnitSubscription *usub = NULL;
337✔
376
                UnitSubscription *next_usub = NULL;
337✔
377
                LIST_FOREACH_SAFE(subs, usub, next_usub, usubs->subs) {
674✔
378
                        Subscription *sub = usub->sub;
337✔
379
                        hashmap_set(unique_subs, &sub);
337✔
380
                        if (hashmap_oom(unique_subs)) {
337✔
381
                                bc_log_error("Failed to compute vector of unique monitors, OOM");
×
382

383
                                hashmap_free(unique_subs);
×
384
                                unique_subs = NULL;
×
385
                                return NULL;
×
386
                        }
387
                }
388
        }
389

390
        /* Only check for wildcards if the unit itself is not one. */
391
        if (!streq(unit, SYMBOL_WILDCARD)) {
380✔
392
                const UnitSubscriptionsKey wildcard_key = { (char *) SYMBOL_WILDCARD };
372✔
393
                const UnitSubscriptions *usubs_wildcard = hashmap_get(node->unit_subscriptions, &wildcard_key);
372✔
394
                if (usubs_wildcard != NULL) {
372✔
395
                        UnitSubscription *usub = NULL;
42✔
396
                        UnitSubscription *next_usub = NULL;
42✔
397
                        LIST_FOREACH_SAFE(subs, usub, next_usub, usubs_wildcard->subs) {
84✔
398
                                Subscription *sub = usub->sub;
42✔
399
                                hashmap_set(unique_subs, &sub);
42✔
400
                                if (hashmap_oom(unique_subs)) {
42✔
401
                                        bc_log_error("Failed to compute vector of unique monitors, OOM");
×
402

403
                                        hashmap_free(unique_subs);
×
404
                                        unique_subs = NULL;
×
405
                                        return NULL;
×
406
                                }
407
                        }
408
                }
409
        }
410

411
        return unique_subs;
412
}
413

414

415
static int node_match_job_state_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
7✔
416
        Node *node = userdata;
7✔
417
        Controller *controller = node->controller;
7✔
418
        uint32_t bc_job_id = 0;
7✔
419
        const char *state = NULL;
7✔
420

421
        int r = sd_bus_message_read(m, "us", &bc_job_id, &state);
7✔
422
        if (r < 0) {
7✔
423
                bc_log_errorf("Invalid JobStateChange signal: %s", strerror(-r));
×
424
                return 0;
×
425
        }
426

427
        controller_job_state_changed(controller, bc_job_id, state);
7✔
428
        return 1;
429
}
430

431
static int node_match_unit_properties_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
273✔
432
        Node *node = userdata;
273✔
433
        const char *unit = NULL;
273✔
434
        const char *interface = NULL;
273✔
435

436
        int r = sd_bus_message_read(m, "ss", &unit, &interface);
273✔
437
        if (r >= 0) {
273✔
438
                r = sd_bus_message_rewind(m, false);
273✔
439
        }
440
        if (r < 0) {
273✔
441
                bc_log_error("Invalid UnitPropertiesChanged signal");
×
442
                return 0;
×
443
        }
444

445
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
273✔
446
        if (unique_subs != NULL) {
273✔
447
                Subscription **subp = NULL;
273✔
448
                size_t i = 0;
273✔
449
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
819✔
450
                        Subscription *sub = *subp;
273✔
451
                        int r = sub->handle_unit_property_changed(sub->monitor, node->name, unit, interface, m);
273✔
452
                        if (r < 0) {
273✔
453
                                bc_log_error("Failed to emit UnitPropertyChanged signal");
×
454
                        }
455
                }
456
                hashmap_free(unique_subs);
273✔
457
        }
458

459
        return 1;
460
}
461

462

463
static int node_match_unit_new(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
32✔
464
        Node *node = userdata;
32✔
465
        const char *unit = NULL;
32✔
466
        const char *reason = NULL;
32✔
467

468
        int r = sd_bus_message_read(m, "ss", &unit, &reason);
32✔
469
        if (r < 0) {
32✔
470
                bc_log_errorf("Invalid UnitNew signal: %s", strerror(-r));
×
471
                return 0;
×
472
        }
473

474
        const UnitSubscriptionsKey key = { (char *) unit };
32✔
475
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
32✔
476
        if (usubs != NULL) {
32✔
477
                usubs->loaded = true;
30✔
478
                if (is_wildcard(unit)) {
30✔
479
                        usubs->active_state = UNIT_ACTIVE;
6✔
480
                        free(usubs->substate);
6✔
481
                        usubs->substate = strdup("running");
6✔
482
                }
483
        }
484

485
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
32✔
486
        if (unique_subs != NULL) {
32✔
487
                Subscription **subp = NULL;
32✔
488
                size_t i = 0;
32✔
489
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
96✔
490
                        Subscription *sub = *subp;
32✔
491
                        int r = sub->handle_unit_new(sub->monitor, node->name, unit, reason);
32✔
492
                        if (r < 0) {
32✔
493
                                bc_log_errorf("Failed to emit UnitNew signal: %s", strerror(-r));
×
494
                        }
495
                }
496
                hashmap_free(unique_subs);
32✔
497
        }
498

499
        return 1;
500
}
501

502
static int node_match_unit_state_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
53✔
503
        Node *node = userdata;
53✔
504
        const char *unit = NULL;
53✔
505
        const char *active_state = NULL;
53✔
506
        const char *substate = NULL;
53✔
507
        const char *reason = NULL;
53✔
508

509
        int r = sd_bus_message_read(m, "ssss", &unit, &active_state, &substate, &reason);
53✔
510
        if (r < 0) {
53✔
511
                bc_log_errorf("Invalid UnitStateChanged signal: %s", strerror(-r));
×
512
                return 0;
×
513
        }
514

515
        const UnitSubscriptionsKey key = { (char *) unit };
53✔
516
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
53✔
517
        if (usubs != NULL) {
53✔
518
                usubs->loaded = true;
49✔
519
                usubs->active_state = active_state_from_string(active_state);
49✔
520
                free(usubs->substate);
49✔
521
                usubs->substate = strdup(substate);
49✔
522
        }
523

524
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
53✔
525
        if (unique_subs != NULL) {
53✔
526
                Subscription **subp = NULL;
53✔
527
                size_t i = 0;
53✔
528
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
159✔
529
                        Subscription *sub = *subp;
53✔
530
                        int r = sub->handle_unit_state_changed(
106✔
531
                                        sub->monitor, node->name, unit, active_state, substate, reason);
53✔
532
                        if (r < 0) {
53✔
533
                                bc_log_errorf("Failed to emit UnitStateChanged signal: %s", strerror(-r));
×
534
                        }
535
                }
536
                hashmap_free(unique_subs);
53✔
537
        }
538

539
        return 1;
540
}
541

542
static int node_match_unit_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
16✔
543
        Node *node = userdata;
16✔
544
        const char *unit = NULL;
16✔
545

546
        int r = sd_bus_message_read(m, "s", &unit);
16✔
547
        if (r < 0) {
16✔
548
                bc_log_errorf("Invalid UnitRemoved signal: %s", strerror(-r));
×
549
                return 0;
×
550
        }
551

552
        const UnitSubscriptionsKey key = { (char *) unit };
16✔
553
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
16✔
554
        if (usubs != NULL) {
16✔
555
                usubs->loaded = false;
13✔
556
        }
557

558
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
16✔
559
        if (unique_subs != NULL) {
16✔
560
                Subscription **subp = NULL;
16✔
561
                size_t i = 0;
16✔
562
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
47✔
563
                        Subscription *sub = *subp;
15✔
564
                        int r = sub->handle_unit_removed(sub->monitor, node->name, unit, "real");
15✔
565
                        if (r < 0) {
15✔
566
                                bc_log_errorf("Failed to emit UnitRemoved signal: %s", strerror(-r));
×
567
                        }
568
                }
569
                hashmap_free(unique_subs);
16✔
570
        }
571

572
        return 1;
573
}
574

575
static int node_match_job_done(UNUSED sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *error) {
62✔
576
        Node *node = userdata;
62✔
577
        Controller *controller = node->controller;
62✔
578
        uint32_t bc_job_id = 0;
62✔
579
        const char *result = NULL;
62✔
580

581
        int r = sd_bus_message_read(m, "us", &bc_job_id, &result);
62✔
582
        if (r < 0) {
62✔
583
                bc_log_errorf("Invalid JobDone signal: %s", strerror(-r));
×
584
                return 0;
×
585
        }
586

587
        controller_finish_job(controller, bc_job_id, result);
62✔
588
        return 1;
589
}
590

591
static int node_match_heartbeat(UNUSED sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
85✔
592
        Node *node = userdata;
85✔
593
        uint64_t now = 0;
85✔
594
        uint64_t now_monotonic = 0;
85✔
595

596
        now = get_time_micros();
85✔
597
        if (now == USEC_INFINITY) {
85✔
598
                bc_log_error("Failed to get current time on heartbeat");
×
599
                return 0;
×
600
        }
601

602
        now_monotonic = get_time_micros_monotonic();
85✔
603
        if (now_monotonic == USEC_INFINITY) {
85✔
604
                bc_log_error("Failed to get current monotonic time on heartbeat");
×
605
                return 0;
×
606
        }
607

608
        node->last_seen = now;
85✔
609
        node->last_seen_monotonic = now_monotonic;
85✔
610
        return 1;
85✔
611
}
612

613
static ProxyMonitor *node_find_proxy_monitor(Node *node, const char *target_node_name, const char *unit_name) {
22✔
614
        ProxyMonitor *proxy_monitor = NULL;
22✔
615
        LIST_FOREACH(monitors, proxy_monitor, node->proxy_monitors) {
22✔
616
                if (streq(proxy_monitor->target_node->name, target_node_name) &&
8✔
617
                    streq(proxy_monitor->unit_name, unit_name)) {
8✔
618
                        return proxy_monitor;
619
                }
620
        }
621

622
        return NULL;
623
}
624

625
static int node_on_match_proxy_new(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
14✔
626
        Node *node = userdata;
14✔
627
        Controller *controller = node->controller;
14✔
628

629
        const char *target_node_name = NULL;
14✔
630
        const char *unit_name = NULL;
14✔
631
        const char *proxy_object_path = NULL;
14✔
632

633
        int r = sd_bus_message_read(m, "sso", &target_node_name, &unit_name, &proxy_object_path);
14✔
634
        if (r < 0) {
14✔
635
                bc_log_errorf("Invalid arguments in ProxyNew signal: %s", strerror(-r));
×
636
                return r;
×
637
        }
638

639
        bc_log_infof("Node '%s' registered new proxy for unit '%s' on node '%s'",
14✔
640
                     node->name,
641
                     unit_name,
642
                     target_node_name);
643

644
        _cleanup_proxy_monitor_ ProxyMonitor *monitor = proxy_monitor_new(
28✔
645
                        node, target_node_name, unit_name, proxy_object_path);
646
        if (monitor == NULL) {
14✔
647
                bc_log_error("Failed to create proxy monitor, OOM");
×
648
                return -ENOMEM;
649
        }
650

651
        Node *target_node = controller_find_node(controller, target_node_name);
14✔
652
        if (target_node == NULL) {
14✔
653
                bc_log_error("Proxy requested for non-existing node");
1✔
654
                proxy_monitor_send_error(monitor, "No such node");
1✔
655
                return 0;
656
        }
657

658
        ProxyTarget *target = node_find_allowed_proxy_target(node, target_node_name);
13✔
659
        if (target == NULL) {
13✔
660
                bc_log_errorf("Proxy request denied for %s->%s", node->name, target_node_name);
3✔
661
                proxy_monitor_send_error(monitor, "Proxy on node not allowed");
3✔
662
                return 0;
663
        }
664

665
        ProxyMonitor *old_monitor = node_find_proxy_monitor(node, target_node_name, unit_name);
10✔
666
        if (old_monitor != NULL) {
10✔
667
                bc_log_warnf("Proxy for '%s' (on '%s') requested, but old proxy already exists, removing it",
×
668
                             unit_name,
669
                             target_node_name);
670
                node_remove_proxy_monitor(node, old_monitor);
×
671
        }
672

673
        r = proxy_monitor_set_target_node(monitor, target_node);
10✔
674
        if (r < 0) {
10✔
675
                bc_log_errorf("Failed to add proxy dependency: %s", strerror(-r));
×
676
                proxy_monitor_send_error(monitor, "Failed to add proxy dependency");
×
677
                return 0;
678
        }
679

680
        /* We now have a valid monitor, add it to the list and enable monitor.
681
           From this point we should not send errors. */
682
        controller_add_subscription(controller, monitor->subscription);
10✔
683
        LIST_APPEND(monitors, node->proxy_monitors, proxy_monitor_ref(monitor));
10✔
684

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

687
        return 0;
688
}
689

690
void node_remove_proxy_monitor(Node *node, ProxyMonitor *monitor) {
10✔
691
        Controller *controller = node->controller;
10✔
692

693
        proxy_monitor_close(monitor);
10✔
694

695
        controller_remove_subscription(controller, monitor->subscription);
10✔
696
        LIST_REMOVE(monitors, node->proxy_monitors, monitor);
10✔
697

698
        proxy_monitor_unref(monitor);
10✔
699
}
10✔
700

701
static int node_on_match_proxy_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
12✔
702
        Node *node = userdata;
12✔
703
        const char *target_node_name = NULL;
12✔
704
        const char *unit_name = NULL;
12✔
705

706
        int r = sd_bus_message_read(m, "ss", &target_node_name, &unit_name);
12✔
707
        if (r < 0) {
12✔
708
                bc_log_errorf("Invalid arguments in ProxyRemoved signal: %s", strerror(-r));
×
709
                return r;
×
710
        }
711
        bc_log_infof("Node '%s' unregistered proxy for unit '%s' on node '%s'",
12✔
712
                     node->name,
713
                     unit_name,
714
                     target_node_name);
715

716
        ProxyMonitor *proxy_monitor = node_find_proxy_monitor(node, target_node_name, unit_name);
12✔
717
        if (proxy_monitor == NULL) {
12✔
718
                bc_log_error("Got ProxyRemoved for unknown monitor");
4✔
719
                return 0;
4✔
720
        }
721

722
        node_remove_proxy_monitor(node, proxy_monitor);
8✔
723
        return 0;
724
}
725

726
bool node_set_agent_bus(Node *node, sd_bus *bus) {
464✔
727
        int r = 0;
464✔
728

729
        if (node->agent_bus != NULL) {
464✔
730
                bc_log_error("Error: Trying to add two agents for a node");
×
731
                return false;
464✔
732
        }
733

734
        node->agent_bus = sd_bus_ref(bus);
464✔
735

736
        // If getting peer IP fails, only log and proceed as normal.
737
        _cleanup_free_ char *peer_ip = NULL;
464✔
738
        uint16_t peer_port = 0;
464✔
739
        r = get_peer_ip_address(node->agent_bus, &peer_ip, &peer_port);
464✔
740
        if (r < 0 && r != -EINVAL) {
464✔
741
                bc_log_errorf("Failed to get peer IP: %s", strerror(-r));
×
742
        } else {
743
                node->peer_ip = steal_pointer(&peer_ip);
464✔
744
        }
745

746
#ifdef CONFIG_H_USE_SELINUX
747
        char *peercon = NULL;
464✔
748
        if (getpeercon(sd_bus_get_fd(bus), &peercon) == 0) {
464✔
749
                node->peer_selinux_context = parse_selinux_type(peercon);
220✔
750
                if (node->peer_selinux_context == NULL) {
220✔
751
                        bc_log_errorf("Failed to parse peer selinux type '%s'", peercon);
220✔
752
                }
753
                freecon(peercon);
220✔
754
        }
755
#endif
756

757
        if (node->name == NULL) {
464✔
758
                // We only connect to this on the unnamed nodes so register
759
                // can be called. We can't reconnect it during migration.
760
                r = sd_bus_add_object_vtable(
233✔
761
                                bus,
762
                                &node->internal_controller_slot,
763
                                INTERNAL_CONTROLLER_OBJECT_PATH,
764
                                INTERNAL_CONTROLLER_INTERFACE,
765
                                internal_controller_controller_vtable,
766
                                node);
767
                if (r < 0) {
233✔
768
                        node_unset_agent_bus(node);
×
769
                        bc_log_errorf("Failed to add peer bus vtable: %s", strerror(-r));
×
770
                        return false;
771
                }
772
        } else {
773
                // Only listen to signals on named nodes
774
                r = sd_bus_match_signal(
231✔
775
                                bus,
776
                                NULL,
777
                                NULL,
778
                                INTERNAL_AGENT_OBJECT_PATH,
779
                                INTERNAL_AGENT_INTERFACE,
780
                                "JobDone",
781
                                node_match_job_done,
782
                                node);
783
                if (r < 0) {
231✔
784
                        bc_log_errorf("Failed to add JobDone peer bus match: %s", strerror(-r));
×
785
                        return false;
786
                }
787

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

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

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

830
                r = sd_bus_match_signal(
231✔
831
                                bus,
832
                                NULL,
833
                                NULL,
834
                                INTERNAL_AGENT_OBJECT_PATH,
835
                                INTERNAL_AGENT_INTERFACE,
836
                                "UnitStateChanged",
837
                                node_match_unit_state_changed,
838
                                node);
839
                if (r < 0) {
231✔
840
                        bc_log_errorf("Failed to add UnitStateChanged peer bus match: %s", strerror(-r));
×
841
                        return false;
842
                }
843

844
                r = sd_bus_match_signal(
231✔
845
                                bus,
846
                                NULL,
847
                                NULL,
848
                                INTERNAL_AGENT_OBJECT_PATH,
849
                                INTERNAL_AGENT_INTERFACE,
850
                                "UnitRemoved",
851
                                node_match_unit_removed,
852
                                node);
853
                if (r < 0) {
231✔
854
                        bc_log_errorf("Failed to add UnitRemoved peer bus match: %s", strerror(-r));
×
855
                        return false;
856
                }
857

858
                r = sd_bus_match_signal(
231✔
859
                                bus,
860
                                NULL,
861
                                NULL,
862
                                INTERNAL_AGENT_OBJECT_PATH,
863
                                INTERNAL_AGENT_INTERFACE,
864
                                "ProxyNew",
865
                                node_on_match_proxy_new,
866
                                node);
867
                if (r < 0) {
231✔
868
                        bc_log_errorf("Failed to add ProxyNew peer bus match: %s", strerror(-r));
×
869
                        return false;
870
                }
871

872
                r = sd_bus_match_signal(
231✔
873
                                bus,
874
                                NULL,
875
                                NULL,
876
                                INTERNAL_AGENT_OBJECT_PATH,
877
                                INTERNAL_AGENT_INTERFACE,
878
                                "ProxyRemoved",
879
                                node_on_match_proxy_removed,
880
                                node);
881
                if (r < 0) {
231✔
882
                        bc_log_errorf("Failed to add ProxyNew peer bus match: %s", strerror(-r));
×
883
                        return false;
884
                }
885

886
                r = sd_bus_emit_properties_changed(
462✔
887
                                node->controller->api_bus, node->object_path, NODE_INTERFACE, "Status", NULL);
231✔
888
                if (r < 0) {
231✔
889
                        bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
890
                }
891

892
                r = sd_bus_match_signal(
231✔
893
                                bus,
894
                                NULL,
895
                                NULL,
896
                                INTERNAL_AGENT_OBJECT_PATH,
897
                                INTERNAL_AGENT_INTERFACE,
898
                                AGENT_HEARTBEAT_SIGNAL_NAME,
899
                                node_match_heartbeat,
900
                                node);
901
                if (r < 0) {
231✔
902
                        bc_log_errorf("Failed to add heartbeat signal match: %s", strerror(-r));
×
903
                        return false;
904
                }
905
        }
906

907
        r = sd_bus_match_signal_async(
464✔
908
                        bus,
909
                        &node->disconnect_slot,
910
                        "org.freedesktop.DBus.Local",
911
                        "/org/freedesktop/DBus/Local",
912
                        "org.freedesktop.DBus.Local",
913
                        "Disconnected",
914
                        node_disconnected,
915
                        NULL,
916
                        node);
917
        if (r < 0) {
464✔
918
                node_unset_agent_bus(node);
×
919
                bc_log_errorf("Failed to request match for Disconnected message: %s", strerror(-r));
×
920
                return false;
921
        }
922

923
        if (DEBUG_AGENT_MESSAGES) {
464✔
924
                sd_bus_add_filter(bus, NULL, debug_messages_handler, node);
925
        }
926

927

928
        /* Register any active subscriptions with new agent */
929
        node_send_agent_subscribe_all(node);
464✔
930

931
        /* Register any active dependencies with new agent */
932
        node_start_proxy_dependency_all(node);
464✔
933

934
        return true;
935
}
936

937
void node_unset_agent_bus(Node *node) {
1,038✔
938
        bool was_online = node->name && node_has_agent(node);
1,038✔
939

940
        sd_bus_slot_unrefp(&node->disconnect_slot);
1,038✔
941
        node->disconnect_slot = NULL;
1,038✔
942

943
        sd_bus_slot_unrefp(&node->internal_controller_slot);
1,038✔
944
        node->internal_controller_slot = NULL;
1,038✔
945

946
        sd_bus_slot_unrefp(&node->metrics_matching_slot);
1,038✔
947
        node->metrics_matching_slot = NULL;
1,038✔
948

949
        sd_bus_unrefp(&node->agent_bus);
1,038✔
950
        node->agent_bus = NULL;
1,038✔
951

952
        free_and_null(node->peer_selinux_context);
1,038✔
953
        free_and_null(node->peer_ip);
1,038✔
954

955
        if (was_online) {
1,038✔
956
                int r = sd_bus_emit_properties_changed(
462✔
957
                                node->controller->api_bus, node->object_path, NODE_INTERFACE, "Status", NULL);
231✔
958
                if (r < 0) {
231✔
959
                        bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
960
                }
961
        }
962
}
1,038✔
963

964
/* org.eclipse.bluechi.internal.Controller.Register(in s name)) */
965
static int node_method_register(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
232✔
966
        Node *node = userdata;
232✔
967
        Controller *controller = node->controller;
232✔
968
        char *name = NULL;
232✔
969
        _cleanup_free_ char *description = NULL;
232✔
970

971
        /* Once we're not anonymous, don't allow register calls */
972
        if (node->name != NULL) {
232✔
973
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ADDRESS_IN_USE, "Can't register twice");
×
974
        }
975

976
        /* Read the parameters */
977
        int r = sd_bus_message_read(m, "s", &name);
232✔
978
        if (r < 0) {
232✔
979
                bc_log_errorf("Failed to parse parameters: %s", strerror(-r));
×
980
                return r;
981
        }
982

983
        Node *named_node = controller_find_node(controller, name);
232✔
984
        if (named_node == NULL) {
232✔
985
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_SERVICE_UNKNOWN, "Unexpected node name");
1✔
986
        }
987

988
        if (named_node->required_selinux_context &&
231✔
989
            (node->peer_selinux_context == NULL ||
×
990
             !streq(node->peer_selinux_context, named_node->required_selinux_context))) {
×
991
                bc_log_errorf("Node tried to register as '%s' with wrong selinux context '%s', expected '%s'",
×
992
                              name,
993
                              node->peer_selinux_context ? node->peer_selinux_context : "(missing)",
994
                              named_node->required_selinux_context);
995

996
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Node name not allowed");
×
997
        }
998

999
        if (node_has_agent(named_node)) {
231✔
1000
                return sd_bus_reply_method_errorf(
×
1001
                                m, SD_BUS_ERROR_ADDRESS_IN_USE, "The node is already connected");
1002
        }
1003

1004
        named_node->last_seen = get_time_micros();
231✔
1005
        named_node->last_seen_monotonic = get_time_micros_monotonic();
231✔
1006

1007
        r = asprintf(&description, "node-%s", name);
231✔
1008
        if (r >= 0) {
231✔
1009
                (void) sd_bus_set_description(node->agent_bus, description);
231✔
1010
        }
1011

1012
        /* Migrate the agent connection to the named node */
1013
        _cleanup_sd_bus_ sd_bus *agent_bus = sd_bus_ref(node->agent_bus);
463✔
1014
        if (!node_set_agent_bus(named_node, agent_bus)) {
231✔
1015
                return sd_bus_reply_method_errorf(
×
1016
                                m, SD_BUS_ERROR_FAILED, "Internal error: Couldn't set agent bus");
1017
        }
1018

1019
        if (controller->metrics_enabled) {
231✔
1020
                node_enable_metrics(named_node);
×
1021
        }
1022

1023
        node_unset_agent_bus(node);
231✔
1024

1025
        /* update number of online nodes and check the new system state */
1026
        controller_check_system_status(controller, controller->number_of_nodes_online++);
231✔
1027

1028
        bc_log_infof("Registered managed node from fd %d as '%s'", sd_bus_get_fd(agent_bus), name);
231✔
1029

1030
        return sd_bus_reply_method_return(m, "");
231✔
1031
}
1032

1033
static int node_disconnected(UNUSED sd_bus_message *message, void *userdata, UNUSED sd_bus_error *error) {
207✔
1034
        Node *node = userdata;
207✔
1035

1036
        node_disconnect(node);
207✔
1037

1038
        return 0;
207✔
1039
}
1040

1041
void node_disconnect(Node *node) {
208✔
1042
        Controller *controller = node->controller;
208✔
1043
        void *item = NULL;
208✔
1044
        size_t i = 0;
208✔
1045

1046
        /* Send virtual unit remove and state change for any reported loaded units */
1047
        while (hashmap_iter(node->unit_subscriptions, &i, &item)) {
211✔
1048
                UnitSubscriptions *usubs = item;
3✔
1049
                bool send_state_change = false;
3✔
1050

1051
                if (!usubs->loaded) {
3✔
1052
                        continue;
×
1053
                }
1054

1055
                if (usubs->active_state >= 0 && usubs->active_state != UNIT_INACTIVE) {
3✔
1056
                        /* We previously reported an not-inactive valid state, send a virtual inactive state */
1057
                        usubs->active_state = UNIT_INACTIVE;
3✔
1058
                        free(usubs->substate);
3✔
1059
                        usubs->substate = strdup("agent-offline");
3✔
1060
                        send_state_change = true;
3✔
1061
                }
1062

1063
                usubs->loaded = false;
3✔
1064

1065
                int r = 0;
3✔
1066
                if (send_state_change) {
×
1067
                        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(
6✔
1068
                                        node, usubs->unit);
3✔
1069
                        if (unique_subs != NULL) {
3✔
1070

1071
                                Subscription **subp = NULL;
3✔
1072
                                size_t s = 0;
3✔
1073
                                while (hashmap_iter(unique_subs, &s, (void **) &subp)) {
9✔
1074
                                        Subscription *sub = *subp;
3✔
1075
                                        r = sub->handle_unit_state_changed(
9✔
1076
                                                        sub->monitor,
1077
                                                        node->name,
3✔
1078
                                                        usubs->unit,
3✔
1079
                                                        active_state_to_string(usubs->active_state),
1080
                                                        usubs->substate,
3✔
1081
                                                        "virtual");
1082
                                        if (r < 0) {
3✔
1083
                                                bc_log_error("Failed to emit UnitStateChanged signal");
×
1084
                                        }
1085
                                }
1086
                                hashmap_free(unique_subs);
3✔
1087
                        }
1088
                }
1089

1090

1091
                struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, usubs->unit);
3✔
1092
                if (unique_subs != NULL) {
3✔
1093

1094
                        Subscription **subp = NULL;
3✔
1095
                        size_t s = 0;
3✔
1096
                        while (hashmap_iter(unique_subs, &s, (void **) &subp)) {
9✔
1097
                                Subscription *sub = *subp;
3✔
1098
                                r = sub->handle_unit_removed(sub->monitor, node->name, usubs->unit, "virtual");
3✔
1099
                                if (r < 0) {
3✔
1100
                                        bc_log_error("Failed to emit UnitRemoved signal");
×
1101
                                }
1102
                        }
1103
                        hashmap_free(unique_subs);
3✔
1104
                }
1105
        }
1106

1107
        ProxyMonitor *proxy_monitor = NULL;
208✔
1108
        ProxyMonitor *next_proxy_monitor = NULL;
208✔
1109
        LIST_FOREACH_SAFE(monitors, proxy_monitor, next_proxy_monitor, node->proxy_monitors) {
210✔
1110
                node_remove_proxy_monitor(node, proxy_monitor);
2✔
1111
        }
1112

1113
        /* Remove anonymous nodes when they disconnect */
1114
        if (node->name == NULL) {
208✔
1115
                bc_log_info("Anonymous node disconnected");
2✔
1116
                controller_remove_node(controller, node);
2✔
1117
        } else {
1118
                bc_log_infof("Node '%s' disconnected", node->name);
206✔
1119
                /* Remove all jobs associated with the registered node that got disconnected. */
1120
                if (!LIST_IS_EMPTY(controller->jobs)) {
206✔
1121
                        Job *job = NULL;
1122
                        Job *next_job = NULL;
4✔
1123
                        LIST_FOREACH_SAFE(jobs, job, next_job, controller->jobs) {
4✔
1124
                                if (strcmp(job->node->name, node->name) == 0) {
2✔
1125
                                        bc_log_debugf("Removing job %d from node %s", job->id, job->node->name);
2✔
1126
                                        LIST_REMOVE(jobs, controller->jobs, job);
2✔
1127
                                        job_unref(job);
2✔
1128
                                }
1129
                        }
1130
                }
1131
                node_unset_agent_bus(node);
206✔
1132

1133
                /* update number of online nodes and check the new system state */
1134
                controller_check_system_status(controller, controller->number_of_nodes_online--);
206✔
1135
        }
1136
}
208✔
1137

1138
const char *node_get_status(Node *node) {
520✔
1139
        if (node_has_agent(node)) {
520✔
1140
                return "online";
271✔
1141
        }
1142
        return "offline";
1143
}
1144

1145
static int node_property_get_status(
485✔
1146
                UNUSED sd_bus *bus,
1147
                UNUSED const char *path,
1148
                UNUSED const char *interface,
1149
                UNUSED const char *property,
1150
                sd_bus_message *reply,
1151
                void *userdata,
1152
                UNUSED sd_bus_error *ret_error) {
1153
        Node *node = userdata;
485✔
1154
        return sd_bus_message_append(reply, "s", node_get_status(node));
485✔
1155
}
1156

1157
static int node_property_get_peer_ip(
3✔
1158
                UNUSED sd_bus *bus,
1159
                UNUSED const char *path,
1160
                UNUSED const char *interface,
1161
                UNUSED const char *property,
1162
                sd_bus_message *reply,
1163
                void *userdata,
1164
                UNUSED sd_bus_error *ret_error) {
1165
        Node *node = userdata;
3✔
1166
        return sd_bus_message_append(reply, "s", node->peer_ip);
3✔
1167
}
1168

1169
AgentRequest *agent_request_ref(AgentRequest *req) {
117✔
1170
        req->ref_count++;
117✔
1171
        return req;
117✔
1172
}
1173

1174
void agent_request_unref(AgentRequest *req) {
234✔
1175
        req->ref_count--;
234✔
1176
        if (req->ref_count != 0) {
234✔
1177
                return;
1178
        }
1179

1180
        if (req->userdata && req->free_userdata) {
117✔
1181
                req->free_userdata(req->userdata);
112✔
1182
        }
1183
        sd_bus_slot_unrefp(&req->slot);
117✔
1184
        sd_bus_message_unrefp(&req->message);
117✔
1185

1186
        Node *node = req->node;
117✔
1187
        LIST_REMOVE(outstanding_requests, node->outstanding_requests, req);
117✔
1188
        node_unref(req->node);
117✔
1189
        free(req);
117✔
1190
}
1191

1192
int node_create_request(
117✔
1193
                AgentRequest **ret,
1194
                Node *node,
1195
                const char *method,
1196
                agent_request_response_t cb,
1197
                void *userdata,
1198
                free_func_t free_userdata) {
1199
        AgentRequest *req = malloc0(sizeof(AgentRequest));
117✔
1200
        if (req == NULL) {
117✔
1201
                return -ENOMEM;
1202
        }
1203

1204
        int r = sd_bus_message_new_method_call(
117✔
1205
                        node->agent_bus,
1206
                        &req->message,
1207
                        BC_AGENT_DBUS_NAME,
1208
                        INTERNAL_AGENT_OBJECT_PATH,
1209
                        INTERNAL_AGENT_INTERFACE,
1210
                        method);
1211
        if (r < 0) {
117✔
1212
                free(req);
×
1213
                req = NULL;
×
1214
                return r;
×
1215
        }
1216

1217
        req->ref_count = 1;
117✔
1218
        req->node = node_ref(node);
117✔
1219
        LIST_INIT(outstanding_requests, req);
117✔
1220
        req->cb = cb;
117✔
1221
        req->userdata = userdata;
117✔
1222
        req->free_userdata = free_userdata;
117✔
1223
        req->is_cancelled = false;
117✔
1224
        LIST_APPEND(outstanding_requests, node->outstanding_requests, req);
117✔
1225

1226
        *ret = req;
117✔
1227
        return 0;
117✔
1228
}
1229

1230
static int agent_request_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
117✔
1231
        _cleanup_agent_request_ AgentRequest *req = userdata;
117✔
1232
        if (req->is_cancelled) {
117✔
1233
                bc_log_debugf("Response received to a cancelled request for node %s. Dropping message.",
×
1234
                              req->node->name);
1235
                return 0;
1236
        }
1237

1238
        return req->cb(req, m, ret_error);
117✔
1239
}
1240

1241
int agent_request_cancel(AgentRequest *r) {
×
1242
        _cleanup_agent_request_ AgentRequest *req = r;
×
1243
        req->is_cancelled = true;
×
1244
        _cleanup_sd_bus_message_ sd_bus_message *m = NULL;
×
1245
        sd_bus_message_new_method_errorf(req->message, &m, SD_BUS_ERROR_FAILED, "Request cancelled");
×
1246

1247
        return req->cb(req, m, NULL);
×
1248
}
1249

1250
int agent_request_start(AgentRequest *req) {
117✔
1251
        Node *node = req->node;
117✔
1252

1253
        int r = sd_bus_call_async(
117✔
1254
                        node->agent_bus,
1255
                        &req->slot,
1256
                        req->message,
1257
                        agent_request_callback,
1258
                        req,
1259
                        BC_DEFAULT_DBUS_TIMEOUT);
1260
        if (r < 0) {
117✔
1261
                return r;
1262
        }
1263

1264
        agent_request_ref(req); /* Keep alive while operation is outstanding */
117✔
1265
        return 1;
117✔
1266
}
1267

1268
AgentRequest *node_request_list_units(
4✔
1269
                Node *node, agent_request_response_t cb, void *userdata, free_func_t free_userdata) {
1270
        if (!node_has_agent(node)) {
4✔
1271
                return NULL;
4✔
1272
        }
1273

1274
        _cleanup_agent_request_ AgentRequest *req = NULL;
4✔
1275
        node_create_request(&req, node, "ListUnits", cb, userdata, free_userdata);
4✔
1276
        if (req == NULL) {
4✔
1277
                return NULL;
1278
        }
1279

1280
        if (agent_request_start(req) < 0) {
4✔
1281
                return NULL;
1282
        }
1283

1284
        return steal_pointer(&req);
4✔
1285
}
1286

1287
AgentRequest *node_request_list_unit_files(
5✔
1288
                Node *node, agent_request_response_t cb, void *userdata, free_func_t free_userdata) {
1289
        if (!node_has_agent(node)) {
5✔
1290
                return NULL;
5✔
1291
        }
1292

1293
        _cleanup_agent_request_ AgentRequest *req = NULL;
5✔
1294
        node_create_request(&req, node, "ListUnitFiles", cb, userdata, free_userdata);
5✔
1295
        if (req == NULL) {
5✔
1296
                return NULL;
1297
        }
1298

1299
        if (agent_request_start(req) < 0) {
5✔
1300
                return NULL;
1301
        }
1302

1303
        return steal_pointer(&req);
5✔
1304
}
1305

1306
/*************************************************************************
1307
 ********** org.eclipse.bluechi.Node.ListUnits **************************
1308
 ************************************************************************/
1309

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

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

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

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

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

1340

1341
static int node_method_list_units(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_units(
2✔
1350
                        node,
1351
                        method_list_units_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 units not found");
×
1356
        }
1357

1358
        return 1;
1359
}
1360

1361
/*************************************************************************
1362
 ********** org.eclipse.bluechi.Node.ListUnitFiles ***********************
1363
 ************************************************************************/
1364

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

1368
        if (sd_bus_message_is_method_error(m, NULL)) {
2✔
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;
2✔
1374
        int r = sd_bus_message_new_method_return(request_message, &reply);
2✔
1375
        if (r < 0) {
2✔
1376
                return sd_bus_reply_method_errorf(
×
1377
                                request_message,
1378
                                SD_BUS_ERROR_FAILED,
1379
                                "Failed to create a reply message for ListUnitFiles request: %s",
1380
                                strerror(-r));
1381
        }
1382

1383
        r = sd_bus_message_copy(reply, m, true);
2✔
1384
        if (r < 0) {
2✔
1385
                return sd_bus_reply_method_errorf(
×
1386
                                request_message,
1387
                                SD_BUS_ERROR_FAILED,
1388
                                "Failed to copy the bus message for ListUnitFiles request: %s",
1389
                                strerror(-r));
1390
        }
1391

1392
        return sd_bus_message_send(reply);
2✔
1393
}
1394

1395
static int node_method_list_unit_files(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2✔
1396
        Node *node = userdata;
2✔
1397

1398
        if (node->is_shutdown) {
2✔
1399
                return sd_bus_reply_method_errorf(
×
1400
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1401
        }
1402

1403
        _cleanup_agent_request_ AgentRequest *agent_req = node_request_list_unit_files(
2✔
1404
                        node,
1405
                        method_list_unit_files_callback,
1406
                        sd_bus_message_ref(m),
2✔
1407
                        (free_func_t) sd_bus_message_unref);
1408
        if (agent_req == NULL) {
2✔
1409
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "List unit files not found");
×
1410
        }
1411
        return 1;
1412
}
1413

1414
/*************************************************************************
1415
 ********** org.eclipse.bluechi.Node.SetUnitProperty ******************
1416
 ************************************************************************/
1417

1418
static int node_method_set_unit_properties_callback(
1✔
1419
                AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
1420
        sd_bus_message *request_message = req->userdata;
1✔
1421

1422
        if (sd_bus_message_is_method_error(m, NULL)) {
1✔
1423
                /* Forward error */
1424
                return sd_bus_reply_method_error(request_message, sd_bus_message_get_error(m));
×
1425
        }
1426

1427
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
1✔
1428
        int r = sd_bus_message_new_method_return(request_message, &reply);
1✔
1429
        if (r < 0) {
1✔
1430
                return sd_bus_reply_method_errorf(
×
1431
                                request_message,
1432
                                SD_BUS_ERROR_FAILED,
1433
                                "Failed to create a reply message: %s",
1434
                                strerror(-r));
1435
        }
1436

1437
        return sd_bus_message_send(reply);
1✔
1438
}
1439

1440
static int node_method_set_unit_properties(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1441
        Node *node = userdata;
1✔
1442
        const char *unit = NULL;
1✔
1443
        int runtime = 0;
1✔
1444

1445
        if (node->is_shutdown) {
1✔
1446
                return sd_bus_reply_method_errorf(
×
1447
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1448
        }
1449

1450
        int r = sd_bus_message_read(m, "sb", &unit, &runtime);
1✔
1451
        if (r < 0) {
1✔
1452
                return sd_bus_reply_method_errorf(
×
1453
                                m,
1454
                                SD_BUS_ERROR_INVALID_ARGS,
1455
                                "Invalid argument for unit or runtime: %s",
1456
                                strerror(-r));
1457
        }
1458

1459
        _cleanup_agent_request_ AgentRequest *req = NULL;
1✔
1460
        r = node_create_request(
1✔
1461
                        &req,
1462
                        node,
1463
                        "SetUnitProperties",
1464
                        node_method_set_unit_properties_callback,
1465
                        sd_bus_message_ref(m),
1✔
1466
                        (free_func_t) sd_bus_message_unref);
1467
        if (req == NULL) {
1✔
1468
                sd_bus_message_unref(m);
×
1469

1470
                return sd_bus_reply_method_errorf(
×
1471
                                m, SD_BUS_ERROR_FAILED, "Failed to create an agent request: %s", strerror(-r));
1472
        }
1473

1474
        r = sd_bus_message_append(req->message, "sb", unit, runtime);
1✔
1475
        if (r < 0) {
1✔
1476
                return sd_bus_reply_method_errorf(
×
1477
                                m,
1478
                                SD_BUS_ERROR_FAILED,
1479
                                "Failed to append unit and runtime to the message: %s",
1480
                                strerror(-r));
1481
        }
1482

1483
        r = sd_bus_message_copy(req->message, m, false);
1✔
1484
        if (r < 0) {
1✔
1485
                return sd_bus_reply_method_errorf(
×
1486
                                m, SD_BUS_ERROR_FAILED, "Failed to copy a message: %s", strerror(-r));
1487
        }
1488

1489
        r = agent_request_start(req);
1✔
1490
        if (r < 0) {
1✔
1491
                return sd_bus_reply_method_errorf(
×
1492
                                m,
1493
                                SD_BUS_ERROR_FAILED,
1494
                                "Failed to call the method to start the node: %s",
1495
                                strerror(-r));
1496
        }
1497

1498
        return 1;
1499
}
1500

1501
static int node_method_passthrough_to_agent_callback(
39✔
1502
                AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
1503
        sd_bus_message *request_message = req->userdata;
39✔
1504

1505
        if (sd_bus_message_is_method_error(m, NULL)) {
39✔
1506
                return sd_bus_reply_method_error(request_message, sd_bus_message_get_error(m));
2✔
1507
        }
1508

1509
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
39✔
1510
        int r = sd_bus_message_new_method_return(request_message, &reply);
37✔
1511
        if (r < 0) {
37✔
1512
                return sd_bus_reply_method_errorf(
×
1513
                                request_message,
1514
                                SD_BUS_ERROR_FAILED,
1515
                                "Failed to create a reply message: %s",
1516
                                strerror(-r));
1517
        }
1518

1519
        r = sd_bus_message_copy(reply, m, true);
37✔
1520
        if (r < 0) {
37✔
1521
                return sd_bus_reply_method_errorf(
×
1522
                                request_message,
1523
                                SD_BUS_ERROR_FAILED,
1524
                                "Failed to copy a reply message: %s",
1525
                                strerror(-r));
1526
        }
1527

1528
        return sd_bus_message_send(reply);
37✔
1529
}
1530

1531
static int node_method_passthrough_to_agent(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
39✔
1532
        Node *node = userdata;
39✔
1533

1534
        if (node->is_shutdown) {
39✔
1535
                return sd_bus_reply_method_errorf(
×
1536
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1537
        }
1538

1539
        _cleanup_agent_request_ AgentRequest *req = NULL;
39✔
1540
        int r = node_create_request(
39✔
1541
                        &req,
1542
                        node,
1543
                        sd_bus_message_get_member(m),
1544
                        node_method_passthrough_to_agent_callback,
1545
                        sd_bus_message_ref(m),
39✔
1546
                        (free_func_t) sd_bus_message_unref);
1547
        if (req == NULL) {
39✔
1548
                sd_bus_message_unref(m);
×
1549

1550
                return sd_bus_reply_method_errorf(
×
1551
                                m, SD_BUS_ERROR_FAILED, "Failed to create an agent request: %s", strerror(-r));
1552
        }
1553

1554
        r = sd_bus_message_copy(req->message, m, true);
39✔
1555
        if (r < 0) {
39✔
1556
                return sd_bus_reply_method_errorf(
×
1557
                                m, SD_BUS_ERROR_FAILED, "Failed to copy a reply message: %s", strerror(-r));
1558
        }
1559

1560
        r = agent_request_start(req);
39✔
1561
        if (r < 0) {
39✔
1562
                return sd_bus_reply_method_errorf(
×
1563
                                m,
1564
                                SD_BUS_ERROR_FAILED,
1565
                                "Failed to call the method to start the node: %s",
1566
                                strerror(-r));
1567
        }
1568

1569
        return 1;
1570
}
1571

1572
/* Keep track of data related to setting up a job. For example calling
1573
   the initial agent request before we know the job is actually going to
1574
   happen. */
1575
typedef struct {
1576
        int ref_count;
1577
        sd_bus_message *request_message;
1578
        Job *job;
1579
} JobSetup;
1580

1581
static JobSetup *job_setup_ref(JobSetup *setup) {
67✔
1582
        setup->ref_count++;
67✔
1583
        return setup;
67✔
1584
}
1585

1586
static void job_setup_unref(JobSetup *setup) {
134✔
1587
        setup->ref_count--;
134✔
1588
        if (setup->ref_count != 0) {
134✔
1589
                return;
1590
        }
1591

1592
        job_unrefp(&setup->job);
67✔
1593
        sd_bus_message_unrefp(&setup->request_message);
67✔
1594
        free(setup);
67✔
1595
}
1596

1597
DEFINE_CLEANUP_FUNC(JobSetup, job_setup_unref)
67✔
1598
#define _cleanup_job_setup_ _cleanup_(job_setup_unrefp)
1599

1600
static JobSetup *job_setup_new(sd_bus_message *request_message, Node *node, const char *unit, const char *type) {
67✔
1601
        _cleanup_job_setup_ JobSetup *setup = malloc0(sizeof(JobSetup));
67✔
1602
        if (setup == NULL) {
67✔
1603
                return NULL;
1604
        }
1605

1606
        setup->ref_count = 1;
67✔
1607
        setup->request_message = sd_bus_message_ref(request_message);
67✔
1608
        setup->job = job_new(node, unit, type);
67✔
1609
        if (setup->job == NULL) {
67✔
1610
                NULL;
67✔
1611
        }
1612

1613
        return steal_pointer(&setup);
67✔
1614
}
1615

1616
static int unit_lifecycle_method_callback(AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
67✔
1617
        Node *node = req->node;
67✔
1618
        Controller *controller = node->controller;
67✔
1619
        JobSetup *setup = req->userdata;
67✔
1620

1621
        if (sd_bus_message_is_method_error(m, NULL)) {
67✔
1622
                /* Forward error */
1623
                return sd_bus_reply_method_error(setup->request_message, sd_bus_message_get_error(m));
1✔
1624
        }
1625

1626
        if (!controller_add_job(controller, setup->job)) {
66✔
1627
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to add a job");
×
1628
        }
1629

1630
        return sd_bus_reply_method_return(setup->request_message, "o", setup->job->object_path);
66✔
1631
}
1632

1633
static int node_run_unit_lifecycle_method(
67✔
1634
                sd_bus_message *m, Node *node, const char *job_type, const char *method) {
1635
        const char *unit = NULL;
67✔
1636
        const char *mode = NULL;
67✔
1637
        uint64_t start_time = get_time_micros();
67✔
1638

1639
        if (node->is_shutdown) {
67✔
1640
                return sd_bus_reply_method_errorf(
×
1641
                                m, SD_BUS_ERROR_FAILED, "Request not allowed: node is in shutdown state");
1642
        }
1643

1644
        int r = sd_bus_message_read(m, "ss", &unit, &mode);
67✔
1645
        if (r < 0) {
67✔
1646
                return sd_bus_reply_method_errorf(
×
1647
                                m,
1648
                                SD_BUS_ERROR_INVALID_ARGS,
1649
                                "Invalid argument for unit or mode: %s",
1650
                                strerror(-r));
1651
        }
1652

1653
        _cleanup_job_setup_ JobSetup *setup = job_setup_new(m, node, unit, job_type);
134✔
1654
        if (setup == NULL) {
67✔
1655
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "Out of memory");
×
1656
        }
1657

1658
        if (node->controller->metrics_enabled) {
67✔
1659
                setup->job->job_start_micros = start_time;
5✔
1660
        }
1661

1662
        _cleanup_agent_request_ AgentRequest *req = NULL;
67✔
1663
        r = node_create_request(
67✔
1664
                        &req,
1665
                        node,
1666
                        method,
1667
                        unit_lifecycle_method_callback,
1668
                        job_setup_ref(setup),
67✔
1669
                        (free_func_t) job_setup_unref);
1670
        if (req == NULL) {
67✔
1671
                job_setup_unref(setup);
×
1672

1673
                return sd_bus_reply_method_errorf(
×
1674
                                m, SD_BUS_ERROR_FAILED, "Failed to create an agent request: %s", strerror(-r));
1675
        }
1676

1677
        r = sd_bus_message_append(req->message, "ssu", unit, mode, setup->job->id);
67✔
1678
        if (r < 0) {
67✔
1679
                return sd_bus_reply_method_errorf(
×
1680
                                m,
1681
                                SD_BUS_ERROR_FAILED,
1682
                                "Failed to append unit, mode, and job ID to the message: %s",
1683
                                strerror(-r));
1684
        }
1685

1686
        r = agent_request_start(req);
67✔
1687
        if (r < 0) {
67✔
1688
                return sd_bus_reply_method_errorf(
×
1689
                                m,
1690
                                SD_BUS_ERROR_FAILED,
1691
                                "Failed to call the method to start the node: %s",
1692
                                strerror(-r));
1693
        }
1694

1695
        return 1;
1696
}
1697

1698

1699
/*************************************************************************
1700
 ********** org.eclipse.bluechi.Node.StartUnit **************************
1701
 ************************************************************************/
1702

1703
static int node_method_start_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
48✔
1704
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "start", "StartUnit");
48✔
1705
}
1706

1707
/*************************************************************************
1708
 ********** org.eclipse.bluechi.Node.StopUnit ***************************
1709
 ************************************************************************/
1710

1711

1712
static int node_method_stop_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
16✔
1713
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "stop", "StopUnit");
16✔
1714
}
1715

1716
/*************************************************************************
1717
 ********** org.eclipse.bluechi.Node.RestartUnit ************************
1718
 ************************************************************************/
1719

1720
static int node_method_restart_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2✔
1721
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "restart", "RestartUnit");
2✔
1722
}
1723

1724
/*************************************************************************
1725
 ********** org.eclipse.bluechi.Node.ReloadUnit **************************
1726
 ************************************************************************/
1727

1728
static int node_method_reload_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1729
        return node_run_unit_lifecycle_method(m, (Node *) userdata, "reload", "ReloadUnit");
1✔
1730
}
1731

1732
/*************************************************************************
1733
 ********** org.eclipse.bluechi.Node.SetLogLevel *******************
1734
 ************************************************************************/
1735

1736
static int node_method_set_log_level(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) {
3✔
1737
        const char *level = NULL;
3✔
1738
        Node *node = (Node *) userdata;
3✔
1739
        sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
1740
        _cleanup_sd_bus_message_ sd_bus_message *sub_m = NULL;
3✔
1741

1742
        int r = sd_bus_message_read(m, "s", &level);
3✔
1743
        if (r < 0) {
3✔
1744
                return sd_bus_reply_method_errorf(
×
1745
                                m,
1746
                                SD_BUS_ERROR_INVALID_ARGS,
1747
                                "Invalid argument for the log-level: %s",
1748
                                strerror(-r));
1749
        }
1750
        LogLevel loglevel = string_to_log_level(level);
3✔
1751
        if (loglevel == LOG_LEVEL_INVALID) {
3✔
1752
                r = sd_bus_reply_method_return(m, "");
1✔
1753
                if (r < 0) {
1✔
1754
                        return sd_bus_reply_method_errorf(
×
1755
                                        m,
1756
                                        SD_BUS_ERROR_INVALID_ARGS,
1757
                                        "Invalid argument for the log level invalid");
1758
                }
1759
        }
1760
        r = sd_bus_call_method(
3✔
1761
                        node->agent_bus,
1762
                        BC_AGENT_DBUS_NAME,
1763
                        INTERNAL_AGENT_OBJECT_PATH,
1764
                        INTERNAL_AGENT_INTERFACE,
1765
                        "SetLogLevel",
1766
                        &error,
1767
                        &sub_m,
1768
                        "s",
1769
                        level);
1770
        if (r < 0) {
3✔
1771
                bc_log_errorf("Failed to set log level call: %s", error.message);
1✔
1772
                sd_bus_error_free(&error);
1✔
1773
                return sd_bus_reply_method_errorf(
1✔
1774
                                m,
1775
                                SD_BUS_ERROR_FAILED,
1776
                                "Failed to call a method to set the log level: %s",
1777
                                strerror(-r));
1778
        }
1779
        return sd_bus_reply_method_return(m, "");
2✔
1780
}
1781

1782
static int send_agent_simple_message(Node *node, const char *method, const char *arg) {
78✔
1783
        _cleanup_sd_bus_message_ sd_bus_message *m = NULL;
78✔
1784
        int r = sd_bus_message_new_method_call(
78✔
1785
                        node->agent_bus,
1786
                        &m,
1787
                        BC_AGENT_DBUS_NAME,
1788
                        INTERNAL_AGENT_OBJECT_PATH,
1789
                        INTERNAL_AGENT_INTERFACE,
1790
                        method);
1791
        if (r < 0) {
78✔
1792
                return r;
1793
        }
1794

1795
        if (arg != NULL) {
78✔
1796
                r = sd_bus_message_append(m, "s", arg);
70✔
1797
                if (r < 0) {
70✔
1798
                        return r;
1799
                }
1800
        }
1801

1802
        return sd_bus_send(node->agent_bus, m, NULL);
78✔
1803
}
1804

1805
static void node_send_agent_subscribe(Node *node, const char *unit) {
28✔
1806
        if (!node_has_agent(node)) {
28✔
1807
                return;
1808
        }
1809

1810
        int r = send_agent_simple_message(node, "Subscribe", unit);
28✔
1811
        if (r < 0) {
28✔
1812
                bc_log_error("Failed to subscribe w/ agent");
×
1813
        }
1814
}
1815

1816

1817
static void node_send_agent_unsubscribe(Node *node, const char *unit) {
27✔
1818
        if (!node_has_agent(node)) {
27✔
1819
                return;
1820
        }
1821

1822
        int r = send_agent_simple_message(node, "Unsubscribe", unit);
25✔
1823
        if (r < 0) {
25✔
1824
                bc_log_error("Failed to unsubscribe w/ agent");
×
1825
        }
1826
}
1827

1828
/* Resubscribe to all subscriptions */
1829
static void node_send_agent_subscribe_all(Node *node) {
464✔
1830
        void *item = NULL;
464✔
1831
        size_t i = 0;
464✔
1832

1833
        while (hashmap_iter(node->unit_subscriptions, &i, &item)) {
465✔
1834
                UnitSubscriptions *usubs = item;
1✔
1835
                node_send_agent_subscribe(node, usubs->unit);
1✔
1836
        }
1837
}
464✔
1838

1839
void node_subscribe(Node *node, Subscription *sub) {
27✔
1840
        SubscribedUnit *sub_unit = NULL;
27✔
1841
        SubscribedUnit *next_sub_unit = NULL;
27✔
1842
        LIST_FOREACH_SAFE(units, sub_unit, next_sub_unit, sub->subscribed_units) {
55✔
1843
                const UnitSubscriptionsKey key = { sub_unit->name };
28✔
1844
                UnitSubscriptions *usubs = NULL;
28✔
1845

1846
                _cleanup_free_ UnitSubscription *usub = malloc0(sizeof(UnitSubscription));
28✔
1847
                if (usub == NULL) {
28✔
1848
                        bc_log_error("Failed to subscribe to unit, OOM");
×
1849
                        return;
1850
                }
1851
                usub->sub = sub;
28✔
1852

1853
                usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
28✔
1854
                if (usubs == NULL) {
28✔
1855
                        UnitSubscriptions v = { NULL, NULL, false, _UNIT_ACTIVE_STATE_INVALID, NULL };
27✔
1856
                        v.unit = strdup(key.unit);
27✔
1857
                        if (v.unit == NULL) {
27✔
1858
                                bc_log_error("Failed to subscribe to unit, OOM");
×
1859
                                return;
×
1860
                        }
1861

1862
                        usubs = (UnitSubscriptions *) hashmap_set(node->unit_subscriptions, &v);
27✔
1863
                        if (usubs == NULL && hashmap_oom(node->unit_subscriptions)) {
27✔
1864
                                free(v.unit);
×
1865
                                bc_log_error("Failed to subscribe to unit, OOM");
×
1866
                                return;
1867
                        }
1868

1869
                        /* First sub to this unit, pass to agent */
1870
                        node_send_agent_subscribe(node, sub_unit->name);
27✔
1871

1872
                        usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
27✔
1873
                }
1874

1875
                LIST_APPEND(subs, usubs->subs, steal_pointer(&usub));
28✔
1876

1877
                /* We know this is loaded, so we won't get notified from
1878
                   the agent, instead send a virtual event here. */
1879
                if (usubs->loaded) {
28✔
1880
                        int r = sub->handle_unit_new(sub->monitor, node->name, sub_unit->name, "virtual");
1✔
1881
                        if (r < 0) {
1✔
1882
                                bc_log_error("Failed to emit UnitNew signal");
×
1883
                        }
1884

1885
                        if (usubs->active_state >= 0) {
1✔
1886
                                r = sub->handle_unit_state_changed(
1✔
1887
                                                sub->monitor,
1888
                                                node->name,
1✔
1889
                                                sub_unit->name,
1✔
1890
                                                active_state_to_string(usubs->active_state),
1891
                                                usubs->substate ? usubs->substate : "invalid",
1✔
1892
                                                "virtual");
1893
                                if (r < 0) {
1✔
1894
                                        bc_log_error("Failed to emit UnitNew signal");
×
1895
                                }
1896
                        }
1897
                }
1898
        }
1899
}
1900

1901
void node_unsubscribe(Node *node, Subscription *sub) {
27✔
1902
        SubscribedUnit *sub_unit = NULL;
27✔
1903
        SubscribedUnit *next_sub_unit = NULL;
27✔
1904
        LIST_FOREACH_SAFE(units, sub_unit, next_sub_unit, sub->subscribed_units) {
55✔
1905
                UnitSubscriptionsKey key = { sub_unit->name };
28✔
1906
                UnitSubscriptions *usubs = NULL;
28✔
1907
                UnitSubscription *usub = NULL;
28✔
1908
                UnitSubscription *found = NULL;
28✔
1909
                UnitSubscriptions *deleted = NULL;
28✔
1910

1911
                /* NOTE: If there are errors during subscribe we may still
1912
                   call unsubscribe, so this must silently handle the
1913
                   case of too many unsubscribes. */
1914

1915
                usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
28✔
1916
                if (usubs == NULL) {
28✔
1917
                        continue;
×
1918
                }
1919

1920
                LIST_FOREACH(subs, usub, usubs->subs) {
28✔
1921
                        if (usub->sub == sub) {
28✔
1922
                                found = usub;
1923
                                break;
1924
                        }
1925
                }
1926

1927
                if (found == NULL) {
28✔
1928
                        continue;
×
1929
                }
1930

1931
                LIST_REMOVE(subs, usubs->subs, found);
28✔
1932
                free_and_null(found);
28✔
1933

1934
                if (LIST_IS_EMPTY(usubs->subs)) {
28✔
1935
                        /* Last subscription for this unit, tell agent */
1936
                        node_send_agent_unsubscribe(node, sub_unit->name);
27✔
1937
                        deleted = (UnitSubscriptions *) hashmap_delete(node->unit_subscriptions, &key);
27✔
1938
                        if (deleted) {
27✔
1939
                                unit_subscriptions_clear(deleted);
27✔
1940
                        }
1941
                }
1942
        }
1943
}
27✔
1944

1945
static void node_start_proxy_dependency(Node *node, ProxyDependency *dep) {
10✔
1946
        if (!node_has_agent(node)) {
10✔
1947
                return;
1948
        }
1949

1950
        bc_log_infof("Starting dependency %s on node %s", dep->unit_name, node->name);
10✔
1951

1952
        int r = send_agent_simple_message(node, "StartDep", dep->unit_name);
10✔
1953
        if (r < 0) {
10✔
1954
                bc_log_error("Failed to send StartDep to agent");
×
1955
        }
1956
}
1957

1958
static void node_start_proxy_dependency_all(Node *node) {
464✔
1959
        ProxyDependency *dep = NULL;
464✔
1960
        LIST_FOREACH(deps, dep, node->proxy_dependencies) {
464✔
1961
                node_start_proxy_dependency(node, dep);
×
1962
        }
1963
}
464✔
1964

1965
static void node_stop_proxy_dependency(Node *node, ProxyDependency *dep) {
9✔
1966
        if (!node_has_agent(node)) {
9✔
1967
                return;
1968
        }
1969

1970
        bc_log_infof("Stopping dependency %s on node %s", dep->unit_name, node->name);
7✔
1971

1972
        int r = send_agent_simple_message(node, "StopDep", dep->unit_name);
7✔
1973
        if (r < 0) {
7✔
1974
                bc_log_error("Failed to send StopDep to agent");
×
1975
        }
1976
}
1977

1978
static struct ProxyDependency *node_find_proxy_dependency(Node *node, const char *unit_name) {
20✔
1979
        ProxyDependency *dep = NULL;
20✔
1980
        LIST_FOREACH(deps, dep, node->proxy_dependencies) {
20✔
1981
                if (streq(dep->unit_name, unit_name)) {
11✔
1982
                        return dep;
1983
                }
1984
        }
1985

1986
        return NULL;
1987
}
1988

1989
int node_add_proxy_dependency(Node *node, const char *unit_name) {
10✔
1990
        ProxyDependency *dep = NULL;
10✔
1991

1992
        dep = node_find_proxy_dependency(node, unit_name);
10✔
1993
        if (dep) {
10✔
1994
                dep->n_deps++;
1✔
1995
                /* Always start, if the dep service was stopped by
1996
                   the target service stopping */
1997
                node_start_proxy_dependency(node, dep);
1✔
1998
                return 0;
1✔
1999
        }
2000

2001
        _cleanup_free_ char *unit_name_copy = strdup(unit_name);
9✔
2002
        if (unit_name_copy == NULL) {
9✔
2003
                return -ENOMEM;
2004
        }
2005

2006
        dep = malloc0(sizeof(ProxyDependency));
9✔
2007
        if (dep == NULL) {
9✔
2008
                return -ENOMEM;
2009
        }
2010

2011
        dep->unit_name = steal_pointer(&unit_name_copy);
9✔
2012
        dep->n_deps = 1;
9✔
2013
        LIST_APPEND(deps, node->proxy_dependencies, dep);
9✔
2014

2015
        node_start_proxy_dependency(node, dep);
9✔
2016

2017
        return 0;
2018
}
2019

2020
int node_remove_proxy_dependency(Node *node, const char *unit_name) {
10✔
2021
        ProxyDependency *dep = NULL;
10✔
2022
        dep = node_find_proxy_dependency(node, unit_name);
10✔
2023
        if (!dep) {
10✔
2024
                return -ENOENT;
2025
        }
2026

2027
        dep->n_deps--;
10✔
2028

2029
        if (dep->n_deps == 0) {
10✔
2030
                /* Only stop on the last dep */
2031
                node_stop_proxy_dependency(node, dep);
9✔
2032

2033
                LIST_REMOVE(deps, node->proxy_dependencies, dep);
9✔
2034
                proxy_dependency_free(dep);
9✔
2035
        }
2036

2037
        return 0;
2038
}
2039

2040
int node_method_get_unit_uint64_property_sync(Node *node, char *unit, char *property, uint64_t *value) {
6✔
2041
        int r = 0;
6✔
2042
        _cleanup_sd_bus_message_ sd_bus_message *message = NULL;
6✔
2043
        sd_bus_error error = SD_BUS_ERROR_NULL;
6✔
2044
        r = sd_bus_call_method(
6✔
2045
                        node->agent_bus,
2046
                        BC_AGENT_DBUS_NAME,
2047
                        INTERNAL_AGENT_OBJECT_PATH,
2048
                        INTERNAL_AGENT_INTERFACE,
2049
                        "GetUnitProperty",
2050
                        &error,
2051
                        &message,
2052
                        "sss",
2053
                        unit,
2054
                        "org.freedesktop.systemd1.Unit",
2055
                        property);
2056
        if (r < 0) {
6✔
2057
                bc_log_errorf("Failed to issue GetUnitProperty call: %s", error.message);
×
2058
                sd_bus_error_free(&error);
×
2059
                return r;
2060
        }
2061

2062
        r = sd_bus_message_enter_container(message, SD_BUS_TYPE_VARIANT, "t");
6✔
2063
        if (r < 0) {
6✔
2064
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2065
                return r;
2066
        }
2067

2068
        r = sd_bus_message_read_basic(message, SD_BUS_TYPE_UINT64, value);
6✔
2069
        if (r < 0) {
6✔
2070
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2071
                return r;
2072
        }
2073

2074
        r = sd_bus_message_exit_container(message);
6✔
2075
        if (r < 0) {
6✔
2076
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2077
                return r;
2078
        }
2079

2080
        return 0;
2081
}
2082

2083
void node_enable_metrics(Node *node) {
6✔
2084
        if (!node_has_agent(node)) {
6✔
2085
                return;
2086
        }
2087

2088
        int r = send_agent_simple_message(node, "EnableMetrics", NULL);
6✔
2089
        if (r < 0) {
6✔
2090
                bc_log_error("Failed to enable metrics on agent");
×
2091
        }
2092

2093
        if (!metrics_node_signal_matching_register(node)) {
6✔
2094
                bc_log_error("Failed to enable metrics on agent");
×
2095
        }
2096
}
2097

2098
void node_disable_metrics(Node *node) {
2✔
2099
        if (!node_has_agent(node)) {
2✔
2100
                return;
2101
        }
2102

2103
        int r = send_agent_simple_message(node, "DisableMetrics", NULL);
2✔
2104
        if (r < 0) {
2✔
2105
                bc_log_error("Failed to disable metrics on agent");
×
2106
        }
2107

2108
        sd_bus_slot_unrefp(&node->metrics_matching_slot);
2✔
2109
        node->metrics_matching_slot = NULL;
2✔
2110
}
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

© 2025 Coveralls, Inc