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

eclipse-bluechi / bluechi / 12874115456

20 Jan 2025 06:46PM UTC coverage: 82.236% (-0.1%) from 82.354%
12874115456

push

github

mwperina
Ignore unterminated string initialization for hexchar
gcc v15+ will add the check for unterminated string initialization
as a default, thus causing the build to fail due to the hexchar
function. Since this function uses the char array only for mapping
an integer to a hexadecimal char, this error can be ignored.

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

5597 of 6806 relevant lines covered (82.24%)

1114.87 hits per line

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

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

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

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

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

25
#define DEBUG_AGENT_MESSAGES 0
26

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

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

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

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

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

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

112
typedef struct UnitSubscription UnitSubscription;
113

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

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

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

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

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

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

147
        return strcmp(usubs_a->unit, usubs_b->unit);
504✔
148
}
149

150

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

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

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

177
        node->last_seen = 0;
421✔
178
        node->last_seen_monotonic = 0;
421✔
179

180
        node->name = NULL;
421✔
181
        if (name) {
421✔
182
                node->name = strdup(name);
283✔
183
                if (node->name == NULL) {
283✔
184
                        return NULL;
185
                }
186

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

195
        node->is_shutdown = false;
421✔
196

197
        return steal_pointer(&node);
421✔
198
}
199

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

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

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

217

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

224

225
        node_unset_agent_bus(node);
421✔
226
        sd_bus_slot_unrefp(&node->export_slot);
421✔
227

228
        hashmap_free(node->unit_subscriptions);
421✔
229

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

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

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

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

257
        assert(node->name != NULL);
283✔
258

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

271
        return true;
272
}
273

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

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

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

303

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

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

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

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

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

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

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

364
        return unique_subs;
365
}
366

367

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

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

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

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

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

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

412
        return 1;
413
}
414

415

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

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

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

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

452
        return 1;
453
}
454

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

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

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

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

492
        return 1;
493
}
494

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

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

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

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

525
        return 1;
526
}
527

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

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

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

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

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

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

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

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

575
        return NULL;
576
}
577

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

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

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

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

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

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

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

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

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

632
        return 0;
633
}
634

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

638
        proxy_monitor_close(monitor);
12✔
639

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

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

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

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

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

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

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

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

679
        node->agent_bus = sd_bus_ref(bus);
274✔
680

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

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

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

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

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

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

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

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

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

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

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

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

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

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

872

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

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

879
        return true;
880
}
881

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

885
        sd_bus_slot_unrefp(&node->disconnect_slot);
683✔
886
        node->disconnect_slot = NULL;
683✔
887

888
        sd_bus_slot_unrefp(&node->internal_controller_slot);
683✔
889
        node->internal_controller_slot = NULL;
683✔
890

891
        sd_bus_slot_unrefp(&node->metrics_matching_slot);
683✔
892
        node->metrics_matching_slot = NULL;
683✔
893

894
        sd_bus_unrefp(&node->agent_bus);
683✔
895
        node->agent_bus = NULL;
683✔
896

897
        free_and_null(node->peer_selinux_context);
683✔
898
        free_and_null(node->peer_ip);
683✔
899

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

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

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

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

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

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

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

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

949
        named_node->last_seen = get_time_micros();
136✔
950
        named_node->last_seen_monotonic = get_time_micros_monotonic();
136✔
951

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

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

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

968
        node_unset_agent_bus(node);
136✔
969

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

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

975
        return sd_bus_reply_method_return(m, "");
136✔
976
}
977

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

981
        node_disconnect(node);
127✔
982

983
        return 0;
127✔
984
}
985

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

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

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

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

1008
                usubs->loaded = false;
1✔
1009

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

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

1035

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

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

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

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

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

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

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

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

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

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

1125
        if (req->userdata && req->free_userdata) {
114✔
1126
                req->free_userdata(req->userdata);
110✔
1127
        }
1128
        sd_bus_slot_unrefp(&req->slot);
114✔
1129
        sd_bus_message_unrefp(&req->message);
114✔
1130

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

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

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

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

1171
        *ret = req;
114✔
1172
        return 0;
114✔
1173
}
1174

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

1183
        return req->cb(req, m, ret_error);
114✔
1184
}
1185

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1285

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

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

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

1303
        return 1;
1304
}
1305

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

1310
static int method_list_unit_files_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 ListUnitFiles 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 ListUnitFiles request: %s",
1334
                                strerror(-r));
1335
        }
1336

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1443
        return 1;
1444
}
1445

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

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

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

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

1473
        return sd_bus_message_send(reply);
35✔
1474
}
1475

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

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

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

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

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

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

1514
        return 1;
1515
}
1516

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1640
        return 1;
1641
}
1642

1643

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

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

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

1656

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

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

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

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

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

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

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

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

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

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

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

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

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

1761

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

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

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

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

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

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

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

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

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

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

1820
                LIST_APPEND(subs, usubs->subs, steal_pointer(&usub));
29✔
1821

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

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

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

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

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

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

1872
                if (found == NULL) {
29✔
1873
                        continue;
×
1874
                }
1875

1876
                LIST_REMOVE(subs, usubs->subs, found);
29✔
1877
                free_and_null(found);
29✔
1878

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

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

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

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

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

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

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

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

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

1931
        return NULL;
1932
}
1933

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

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

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

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

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

1960
        node_start_proxy_dependency(node, dep);
11✔
1961

1962
        return 0;
1963
}
1964

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

1972
        dep->n_deps--;
12✔
1973

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

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

1982
        return 0;
1983
}
1984

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

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

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

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

2025
        return 0;
2026
}
2027

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

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

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

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

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

2053
        sd_bus_slot_unrefp(&node->metrics_matching_slot);
1✔
2054
        node->metrics_matching_slot = NULL;
1✔
2055
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc