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

eclipse-bluechi / bluechi / 17978530860

24 Sep 2025 01:38PM UTC coverage: 82.635% (-0.09%) from 82.723%
17978530860

push

github

engelmi
Use /etc/ssh/sshd_config.d for sshd configuration

Instead of updating /etc/ssh/sshd_config with required configuration for
tests it's safer to use /etc/ssh/sshd_config.d/99-bluechi-tests.conf to
prevent overriding required options.

Signed-off-by: Martin Perina <mperina@redhat.com>

5682 of 6876 relevant lines covered (82.64%)

1884.49 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) {
993✔
150
        const UnitSubscriptions *usubs = item;
993✔
151
        return hashmap_sip(usubs->unit, strlen(usubs->unit), seed0, seed1);
993✔
152
}
153

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

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

161

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

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

176
        node->unit_subscriptions = hashmap_new(
604✔
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) {
604✔
186
                return NULL;
187
        }
188

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

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

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

207
        node->is_shutdown = false;
604✔
208

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

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

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

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

229

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

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

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

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

247
        free_and_null(node->name);
604✔
248
        free_and_null(node->object_path);
604✔
249
        free_and_null(node->peer_ip);
604✔
250
        free_and_null(node->required_selinux_context);
604✔
251
        free(node);
604✔
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) {
368✔
302
        Controller *controller = node->controller;
368✔
303

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

306
        int r = sd_bus_add_object_vtable(
736✔
307
                        controller->api_bus,
308
                        &node->export_slot,
309
                        node->object_path,
368✔
310
                        NODE_INTERFACE,
311
                        node_vtable,
312
                        node);
313
        if (r < 0) {
368✔
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,907✔
343
        return node->agent_bus != NULL;
1,907✔
344
}
345

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

350

351
static uint64_t subscription_hashmap_hash(const void *item, UNUSED uint64_t seed0, UNUSED uint64_t seed1) {
381✔
352
        const Subscription * const *subscriptionp = item;
381✔
353
        return (uint64_t) ((uintptr_t) *subscriptionp);
381✔
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) {
381✔
366
        struct hashmap *unique_subs = hashmap_new(
381✔
367
                        sizeof(void *), 0, 0, 0, subscription_hashmap_hash, subscription_hashmap_compare, NULL, NULL);
368
        if (unique_subs == NULL) {
381✔
369
                return NULL;
381✔
370
        }
371

372
        const UnitSubscriptionsKey key = { (char *) unit };
381✔
373
        const UnitSubscriptions *usubs = hashmap_get(node->unit_subscriptions, &key);
381✔
374
        if (usubs != NULL) {
381✔
375
                UnitSubscription *usub = NULL;
339✔
376
                UnitSubscription *next_usub = NULL;
339✔
377
                LIST_FOREACH_SAFE(subs, usub, next_usub, usubs->subs) {
678✔
378
                        Subscription *sub = usub->sub;
339✔
379
                        hashmap_set(unique_subs, &sub);
339✔
380
                        if (hashmap_oom(unique_subs)) {
339✔
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)) {
381✔
392
                const UnitSubscriptionsKey wildcard_key = { (char *) SYMBOL_WILDCARD };
373✔
393
                const UnitSubscriptions *usubs_wildcard = hashmap_get(node->unit_subscriptions, &wildcard_key);
373✔
394
                if (usubs_wildcard != NULL) {
373✔
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) {
33✔
464
        Node *node = userdata;
33✔
465
        const char *unit = NULL;
33✔
466
        const char *reason = NULL;
33✔
467

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

474
        const UnitSubscriptionsKey key = { (char *) unit };
33✔
475
        UnitSubscriptions *usubs = (UnitSubscriptions *) hashmap_get(node->unit_subscriptions, &key);
33✔
476
        if (usubs != NULL) {
33✔
477
                usubs->loaded = true;
31✔
478
                if (is_wildcard(unit)) {
31✔
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);
33✔
486
        if (unique_subs != NULL) {
33✔
487
                Subscription **subp = NULL;
33✔
488
                size_t i = 0;
33✔
489
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
99✔
490
                        Subscription *sub = *subp;
33✔
491
                        int r = sub->handle_unit_new(sub->monitor, node->name, unit, reason);
33✔
492
                        if (r < 0) {
33✔
493
                                bc_log_errorf("Failed to emit UnitNew signal: %s", strerror(-r));
×
494
                        }
495
                }
496
                hashmap_free(unique_subs);
33✔
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) {
52✔
503
        Node *node = userdata;
52✔
504
        const char *unit = NULL;
52✔
505
        const char *active_state = NULL;
52✔
506
        const char *substate = NULL;
52✔
507
        const char *reason = NULL;
52✔
508

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

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

524
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
52✔
525
        if (unique_subs != NULL) {
52✔
526
                Subscription **subp = NULL;
52✔
527
                size_t i = 0;
52✔
528
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
156✔
529
                        Subscription *sub = *subp;
52✔
530
                        int r = sub->handle_unit_state_changed(
104✔
531
                                        sub->monitor, node->name, unit, active_state, substate, reason);
52✔
532
                        if (r < 0) {
52✔
533
                                bc_log_errorf("Failed to emit UnitStateChanged signal: %s", strerror(-r));
×
534
                        }
535
                }
536
                hashmap_free(unique_subs);
52✔
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) {
17✔
543
        Node *node = userdata;
17✔
544
        const char *unit = NULL;
17✔
545

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

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

558
        struct hashmap *unique_subs = node_compute_unique_monitor_subscriptions(node, unit);
17✔
559
        if (unique_subs != NULL) {
17✔
560
                Subscription **subp = NULL;
17✔
561
                size_t i = 0;
17✔
562
                while (hashmap_iter(unique_subs, &i, (void **) &subp)) {
51✔
563
                        Subscription *sub = *subp;
17✔
564
                        int r = sub->handle_unit_removed(sub->monitor, node->name, unit, "real");
17✔
565
                        if (r < 0) {
17✔
566
                                bc_log_errorf("Failed to emit UnitRemoved signal: %s", strerror(-r));
×
567
                        }
568
                }
569
                hashmap_free(unique_subs);
17✔
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) {
96✔
592
        Node *node = userdata;
96✔
593
        uint64_t now = 0;
96✔
594
        uint64_t now_monotonic = 0;
96✔
595

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

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

608
        node->last_seen = now;
96✔
609
        node->last_seen_monotonic = now_monotonic;
96✔
610
        return 1;
96✔
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,041✔
938
        bool was_online = node->name && node_has_agent(node);
1,041✔
939

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

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

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

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

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

955
        if (was_online) {
1,041✔
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,041✔
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) {
518✔
1139
        if (node_has_agent(node)) {
518✔
1140
                return "online";
270✔
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) {
116✔
1170
        req->ref_count++;
116✔
1171
        return req;
116✔
1172
}
1173

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

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

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

1192
int node_create_request(
116✔
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));
116✔
1200
        if (req == NULL) {
116✔
1201
                return -ENOMEM;
1202
        }
1203

1204
        int r = sd_bus_message_new_method_call(
116✔
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) {
116✔
1212
                free(req);
×
1213
                req = NULL;
×
1214
                return r;
×
1215
        }
1216

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

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

1230
static int agent_request_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
116✔
1231
        _cleanup_agent_request_ AgentRequest *req = userdata;
116✔
1232
        if (req->is_cancelled) {
116✔
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);
116✔
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) {
116✔
1251
        Node *node = req->node;
116✔
1252

1253
        int r = sd_bus_call_async(
116✔
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) {
116✔
1261
                return r;
1262
        }
1263

1264
        agent_request_ref(req); /* Keep alive while operation is outstanding */
116✔
1265
        return 1;
116✔
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(
38✔
1502
                AgentRequest *req, sd_bus_message *m, UNUSED sd_bus_error *ret_error) {
1503
        sd_bus_message *request_message = req->userdata;
38✔
1504

1505
        if (sd_bus_message_is_method_error(m, NULL)) {
38✔
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;
38✔
1510
        int r = sd_bus_message_new_method_return(request_message, &reply);
36✔
1511
        if (r < 0) {
36✔
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);
36✔
1520
        if (r < 0) {
36✔
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);
36✔
1529
}
1530

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

1534
        if (node->is_shutdown) {
38✔
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;
38✔
1540
        int r = node_create_request(
38✔
1541
                        &req,
1542
                        node,
1543
                        sd_bus_message_get_member(m),
1544
                        node_method_passthrough_to_agent_callback,
1545
                        sd_bus_message_ref(m),
38✔
1546
                        (free_func_t) sd_bus_message_unref);
1547
        if (req == NULL) {
38✔
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);
38✔
1555
        if (r < 0) {
38✔
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);
38✔
1561
        if (r < 0) {
38✔
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