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

eclipse-bluechi / bluechi / 13115235967

03 Feb 2025 02:04PM UTC coverage: 82.236% (-0.1%) from 82.354%
13115235967

push

github

engelmi
is-online system and agent shouldn't accept arguments

It was found that running
`bluechi-is-online agent --monitor 5000 --wait=10000`
Does not return error, although `--monitor` is a flag. The reason is
that `--monitor` is configured as a flag, and 5000 is treated as an
argument, which the method `agent` is configured to accept (min 0,
max 2), and the same goes for `system` method. Actually, these two
methods do not expect any arguments at all.

This change will limit the usage of `bluechi-is-online agent` and
`bluechi-is-online system` to not expect arguments, only the allowed
options, and `bluechi-is-online node` to accept only one argument

Signed-off-by: Mark Kemel <mkemel@redhat.com>

5597 of 6806 relevant lines covered (82.24%)

1179.34 hits per line

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

82.41
/src/agent/agent.c
1
/*
2
 * Copyright Contributors to the Eclipse BlueChi project
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
#include <arpa/inet.h>
7
#include <errno.h>
8
#include <stdio.h>
9
#include <string.h>
10

11
#include "libbluechi/bus/bus.h"
12
#include "libbluechi/bus/utils.h"
13
#include "libbluechi/common/cfg.h"
14
#include "libbluechi/common/common.h"
15
#include "libbluechi/common/event-util.h"
16
#include "libbluechi/common/network.h"
17
#include "libbluechi/common/opt.h"
18
#include "libbluechi/common/parse-util.h"
19
#include "libbluechi/common/time-util.h"
20
#include "libbluechi/log/log.h"
21
#include "libbluechi/service/shutdown.h"
22

23
#include "agent.h"
24
#include "proxy.h"
25

26
#ifdef USE_USER_API_BUS
27
#        define ALWAYS_USER_API_BUS 1
28
#else
29
#        define ALWAYS_USER_API_BUS 0
30
#endif
31

32
#define DEBUG_SYSTEMD_MESSAGES 0
33
#define DEBUG_SYSTEMD_MESSAGES_CONTENT 0
34

35
typedef struct AgentUnitInfoKey {
36
        char *object_path;
37
} AgentUnitInfoKey;
38

39
struct JobTracker {
40
        char *job_object_path;
41
        job_tracker_callback callback;
42
        void *userdata;
43
        free_func_t free_userdata;
44
        LIST_FIELDS(JobTracker, tracked_jobs);
45
};
46

47
static bool
48
                agent_track_job(Agent *agent,
49
                                const char *job_object_path,
50
                                job_tracker_callback callback,
51
                                void *userdata,
52
                                free_func_t free_userdata);
53

54
/* Keep track of outstanding systemd job and connect it back to
55
   the originating bluechi job id so we can proxy changes to it. */
56
typedef struct {
57
        int ref_count;
58

59
        Agent *agent;
60
        uint32_t bc_job_id;
61
        uint64_t job_start_micros;
62
        char *unit;
63
        char *method;
64
} AgentJobOp;
65

66
static AgentJobOp *agent_job_op_ref(AgentJobOp *op) {
133✔
67
        op->ref_count++;
133✔
68
        return op;
133✔
69
}
70

71
static void agent_job_op_unref(AgentJobOp *op) {
196✔
72
        op->ref_count--;
196✔
73
        if (op->ref_count != 0) {
196✔
74
                return;
75
        }
76

77
        agent_unref(op->agent);
63✔
78
        free_and_null(op->unit);
63✔
79
        free_and_null(op->method);
63✔
80
        free(op);
63✔
81
}
82

83
DEFINE_CLEANUP_FUNC(AgentJobOp, agent_job_op_unref)
67✔
84
#define _cleanup_agent_job_op_ _cleanup_(agent_job_op_unrefp)
85

86
static AgentJobOp *agent_job_new(Agent *agent, uint32_t bc_job_id, const char *unit, const char *method) {
67✔
87
        AgentJobOp *op = malloc0(sizeof(AgentJobOp));
67✔
88
        if (op) {
67✔
89
                op->ref_count = 1;
67✔
90
                op->agent = agent_ref(agent);
67✔
91
                op->bc_job_id = bc_job_id;
67✔
92
                op->job_start_micros = 0;
67✔
93
                op->unit = strdup(unit);
67✔
94
                op->method = strdup(method);
67✔
95
        }
96
        return op;
67✔
97
}
98

99
bool agent_is_connected(Agent *agent) {
348✔
100
        return agent != NULL && agent->connection_state == AGENT_CONNECTION_STATE_CONNECTED;
348✔
101
}
102

103
char *agent_is_online(Agent *agent) {
324✔
104
        if (agent_is_connected(agent)) {
324✔
105
                return "online";
157✔
106
        }
107
        return "offline";
108
}
109

110
static int agent_stop_local_proxy_service(Agent *agent, ProxyService *proxy);
111
static bool agent_connect(Agent *agent);
112
static bool agent_reconnect(Agent *agent);
113
static void agent_peer_bus_close(Agent *agent);
114

115
static int agent_disconnected(UNUSED sd_bus_message *message, void *userdata, UNUSED sd_bus_error *error) {
17✔
116
        Agent *agent = (Agent *) userdata;
17✔
117

118
        bc_log_error("Disconnected from controller");
17✔
119

120
        agent->connection_state = AGENT_CONNECTION_STATE_RETRY;
17✔
121
        int r = sd_bus_emit_properties_changed(
17✔
122
                        agent->api_bus, BC_AGENT_OBJECT_PATH, AGENT_INTERFACE, "Status", NULL);
123
        if (r < 0) {
17✔
124
                bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
125
        }
126

127
        agent->disconnect_timestamp = get_time_micros();
17✔
128
        agent->disconnect_timestamp_monotonic = get_time_micros_monotonic();
17✔
129

130
        /* try to reconnect right away */
131
        agent_reconnect(agent);
17✔
132

133
        return 0;
17✔
134
}
135

136
static int agent_reset_heartbeat_timer(Agent *agent, sd_event_source **event_source);
137

138
static bool agent_check_controller_liveness(Agent *agent) {
36✔
139
        uint64_t diff = 0;
36✔
140
        uint64_t now = 0;
36✔
141

142
        if (agent->controller_heartbeat_threshold_msec <= 0) {
36✔
143
                /* checking liveness by heartbeat disabled since configured threshold is <=0 */
144
                return true;
145
        }
146

147
        now = get_time_micros_monotonic();
3✔
148
        if (now == USEC_INFINITY) {
3✔
149
                bc_log_error("Failed to get the monotonic time");
×
150
                return true;
×
151
        }
152

153
        if (now < agent->controller_last_seen_monotonic) {
3✔
154
                bc_log_error("Clock skew detected");
×
155
                return true;
×
156
        }
157

158
        diff = now - agent->controller_last_seen_monotonic;
3✔
159
        if (diff > (uint64_t) agent->controller_heartbeat_threshold_msec * USEC_PER_MSEC) {
3✔
160
                bc_log_infof("Did not receive heartbeat from controller since '%d'ms. Disconnecting it...",
1✔
161
                             agent->controller_heartbeat_threshold_msec);
162
                agent_disconnected(NULL, agent, NULL);
1✔
163
                return false;
1✔
164
        }
165

166
        return true;
167
}
168

169
static int agent_heartbeat_timer_callback(sd_event_source *event_source, UNUSED uint64_t usec, void *userdata) {
43✔
170
        Agent *agent = (Agent *) userdata;
43✔
171

172
        int r = 0;
43✔
173
        /* Being in CONNECTING state when the timer callback is executed implies that
174
         * the agent hasn't received a reply from the controller yet. In this case,
175
         * we drop the existing register call and the agent is set into RETRY state.*/
176
        if (agent->connection_state == AGENT_CONNECTION_STATE_CONNECTING) {
43✔
177
                bc_log_error("Agent connection attempt failed, retrying");
7✔
178
                if (agent->register_call_slot != NULL) {
7✔
179
                        sd_bus_slot_unrefp(&agent->register_call_slot);
7✔
180
                        agent->register_call_slot = NULL;
7✔
181
                }
182
                agent->connection_state = AGENT_CONNECTION_STATE_RETRY;
7✔
183
        }
184
        if (agent->connection_state == AGENT_CONNECTION_STATE_CONNECTED &&
79✔
185
            agent_check_controller_liveness(agent)) {
36✔
186
                r = sd_bus_emit_signal(
35✔
187
                                agent->peer_dbus,
188
                                INTERNAL_AGENT_OBJECT_PATH,
189
                                INTERNAL_AGENT_INTERFACE,
190
                                "Heartbeat",
191
                                "");
192
                if (r < 0) {
35✔
193
                        bc_log_errorf("Failed to emit heartbeat signal: %s", strerror(-r));
×
194
                }
195
        } else if (agent->connection_state == AGENT_CONNECTION_STATE_RETRY) {
8✔
196
                agent->connection_retry_count++;
7✔
197
                bc_log_infof("Trying to connect to controller (try %d)", agent->connection_retry_count);
7✔
198
                if (!agent_reconnect(agent)) {
7✔
199
                        bc_log_debugf("Connection retry %d failed", agent->connection_retry_count);
×
200
                }
201
        }
202

203
        r = agent_reset_heartbeat_timer(agent, &event_source);
43✔
204
        if (r < 0) {
43✔
205
                bc_log_errorf("Failed to reset agent heartbeat timer: %s", strerror(-r));
×
206
                return r;
×
207
        }
208

209
        return 0;
210
}
211

212
static int agent_reset_heartbeat_timer(Agent *agent, sd_event_source **event_source) {
191✔
213
        return event_reset_time_relative(
382✔
214
                        agent->event,
215
                        event_source,
216
                        CLOCK_BOOTTIME,
217
                        agent->heartbeat_interval_msec * USEC_PER_MSEC,
191✔
218
                        0,
219
                        agent_heartbeat_timer_callback,
220
                        agent,
221
                        0,
222
                        "agent-heartbeat-timer-source",
223
                        false);
224
}
225

226
static int agent_setup_heartbeat_timer(Agent *agent) {
150✔
227
        _cleanup_(sd_event_source_unrefp) sd_event_source *event_source = NULL;
150✔
228
        int r = 0;
150✔
229

230
        assert(agent);
150✔
231

232
        if (agent->heartbeat_interval_msec <= 0) {
150✔
233
                bc_log_warnf("Heartbeat disabled since configured interval '%d' is <=0",
2✔
234
                             agent->heartbeat_interval_msec);
235
                return 0;
236
        }
237

238
        r = agent_reset_heartbeat_timer(agent, &event_source);
148✔
239
        if (r < 0) {
148✔
240
                bc_log_errorf("Failed to reset agent heartbeat timer: %s", strerror(-r));
×
241
                return r;
242
        }
243

244
        return sd_event_source_set_floating(event_source, true);
148✔
245
}
246

247
SystemdRequest *systemd_request_ref(SystemdRequest *req) {
149✔
248
        req->ref_count++;
149✔
249
        return req;
149✔
250
}
251

252
void systemd_request_unref(SystemdRequest *req) {
295✔
253
        Agent *agent = req->agent;
295✔
254

255
        req->ref_count--;
295✔
256
        if (req->ref_count != 0) {
295✔
257
                return;
258
        }
259

260
        if (req->userdata && req->free_userdata) {
146✔
261
                req->free_userdata(req->userdata);
66✔
262
        }
263

264
        sd_bus_message_unrefp(&req->request_message);
146✔
265
        sd_bus_slot_unrefp(&req->slot);
146✔
266
        sd_bus_message_unrefp(&req->message);
146✔
267

268
        LIST_REMOVE(outstanding_requests, agent->outstanding_requests, req);
146✔
269
        agent_unref(req->agent);
146✔
270
        free(req);
146✔
271
}
272

273
static SystemdRequest *agent_create_request_full(
149✔
274
                Agent *agent,
275
                sd_bus_message *request_message,
276
                const char *object_path,
277
                const char *iface,
278
                const char *method) {
279
        _cleanup_systemd_request_ SystemdRequest *req = malloc0(sizeof(SystemdRequest));
149✔
280
        if (req == NULL) {
149✔
281
                return NULL;
282
        }
283

284
        req->ref_count = 1;
149✔
285
        req->agent = agent_ref(agent);
149✔
286
        LIST_INIT(outstanding_requests, req);
149✔
287
        req->request_message = sd_bus_message_ref(request_message);
149✔
288

289
        LIST_APPEND(outstanding_requests, agent->outstanding_requests, req);
149✔
290

291
        int r = sd_bus_message_new_method_call(
149✔
292
                        agent->systemd_dbus, &req->message, SYSTEMD_BUS_NAME, object_path, iface, method);
293
        if (r < 0) {
149✔
294
                bc_log_errorf("Failed to create new bus message: %s", strerror(-r));
×
295
                return NULL;
296
        }
297

298
        return steal_pointer(&req);
299
}
300

301
static SystemdRequest *agent_create_request(Agent *agent, sd_bus_message *request_message, const char *method) {
128✔
302
        return agent_create_request_full(
128✔
303
                        agent, request_message, SYSTEMD_OBJECT_PATH, SYSTEMD_MANAGER_IFACE, method);
304
}
305

306
static void systemd_request_set_userdata(SystemdRequest *req, void *userdata, free_func_t free_userdata) {
67✔
307
        req->userdata = userdata;
67✔
308
        req->free_userdata = free_userdata;
67✔
309
}
67✔
310

311
static bool systemd_request_start(SystemdRequest *req, sd_bus_message_handler_t callback) {
149✔
312
        Agent *agent = req->agent;
149✔
313
        AgentJobOp *op = req->userdata;
149✔
314
        if (op != NULL) {
149✔
315
                op->job_start_micros = get_time_micros();
67✔
316
        }
317

318
        int r = sd_bus_call_async(
149✔
319
                        agent->systemd_dbus, &req->slot, req->message, callback, req, BC_DEFAULT_DBUS_TIMEOUT);
320
        if (r < 0) {
149✔
321
                bc_log_errorf("Failed to call async: %s", strerror(-r));
×
322
                return false;
×
323
        }
324

325
        systemd_request_ref(req); /* outstanding callback owns this ref */
149✔
326
        return true;
149✔
327
}
328

329
static char *make_unit_path(const char *unit) {
26,388✔
330
        _cleanup_free_ char *escaped = bus_path_escape(unit);
26,388✔
331

332
        if (escaped == NULL) {
26,388✔
333
                return NULL;
334
        }
335

336
        return strcat_dup(SYSTEMD_OBJECT_PATH "/unit/", escaped);
26,388✔
337
}
338

339
static void unit_info_clear(void *item) {
25,646✔
340
        AgentUnitInfo *info = item;
25,646✔
341
        free_and_null(info->object_path);
25,646✔
342
        free_and_null(info->unit);
25,646✔
343
        free_and_null(info->substate);
25,646✔
344
}
25,646✔
345

346
static uint64_t unit_info_hash(const void *item, uint64_t seed0, uint64_t seed1) {
99,716✔
347
        const AgentUnitInfo *info = item;
99,716✔
348
        return hashmap_sip(info->object_path, strlen(info->object_path), seed0, seed1);
99,716✔
349
}
350

351
static int unit_info_compare(const void *a, const void *b, UNUSED void *udata) {
47,042✔
352
        const AgentUnitInfo *info_a = a;
47,042✔
353
        const AgentUnitInfo *info_b = b;
47,042✔
354

355
        return strcmp(info_a->object_path, info_b->object_path);
47,042✔
356
}
357

358
static const char *unit_info_get_substate(AgentUnitInfo *info) {
3,068✔
359
        return info->substate ? info->substate : "invalid";
3,068✔
360
}
361

362
struct UpdateStateData {
363
        UnitActiveState active_state;
364
        const char *substate;
365
};
366

367

368
static int unit_info_update_state_cb(const char *key, const char *value_type, sd_bus_message *m, void *userdata) {
32,648✔
369
        struct UpdateStateData *data = userdata;
32,648✔
370
        const char *value = NULL;
32,648✔
371

372
        if (!streq(key, "ActiveState") && !streq(key, "SubState") && !streq(value_type, "s")) {
32,648✔
373
                return 2; /* skip */
32,648✔
374
        }
375

376
        int r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, value_type);
4,452✔
377
        if (r < 0) {
4,452✔
378
                return r;
379
        }
380

381
        r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &value);
4,452✔
382
        if (r < 0) {
4,452✔
383
                return r;
384
        }
385

386
        if (streq(key, "ActiveState")) {
4,452✔
387
                data->active_state = active_state_from_string(value);
1,484✔
388
        } else if (streq(key, "SubState")) {
2,968✔
389
                data->substate = value;
1,484✔
390
        }
391

392
        r = sd_bus_message_exit_container(m);
4,452✔
393
        if (r < 0) {
4,452✔
394
                return r;
×
395
        }
396

397
        return 0;
398
}
399

400

401
/* Updates stored unit state from property change, returns true if the state changes */
402
static bool unit_info_update_state(AgentUnitInfo *info, sd_bus_message *m) {
1,484✔
403
        struct UpdateStateData data = {
1,484✔
404
                info->active_state,
1,484✔
405
                unit_info_get_substate(info),
1,484✔
406
        };
407
        int r = bus_parse_properties_foreach(m, unit_info_update_state_cb, &data);
1,484✔
408
        if (r < 0) {
1,484✔
409
                return false;
1,484✔
410
        }
411

412
        bool changed = false;
1,484✔
413
        if (data.active_state != info->active_state) {
1,484✔
414
                info->active_state = data.active_state;
478✔
415
                changed = true;
478✔
416
        }
417
        if (!streq(data.substate, unit_info_get_substate(info))) {
1,484✔
418
                free(info->substate);
511✔
419
                info->substate = strdup(data.substate);
511✔
420
                changed = true;
511✔
421
        }
422

423
        return changed;
424
}
425

426
Agent *agent_new(void) {
175✔
427
        int r = 0;
175✔
428
        _cleanup_sd_event_ sd_event *event = NULL;
175✔
429
        r = sd_event_default(&event);
175✔
430
        if (r < 0) {
175✔
431
                bc_log_errorf("Failed to create event loop: %s", strerror(-r));
×
432
                return NULL;
433
        }
434

435
        _cleanup_free_ char *service_name = strdup(BC_AGENT_DBUS_NAME);
175✔
436
        if (service_name == NULL) {
175✔
437
                bc_log_error("Out of memory");
×
438
                return NULL;
439
        }
440

441
        _cleanup_free_ SocketOptions *socket_opts = socket_options_new();
350✔
442
        if (socket_opts == NULL) {
175✔
443
                bc_log_error("Out of memory");
×
444
                return NULL;
445
        }
446

447
        struct hashmap *unit_infos = hashmap_new(
175✔
448
                        sizeof(AgentUnitInfo), 0, 0, 0, unit_info_hash, unit_info_compare, unit_info_clear, NULL);
449
        if (unit_infos == NULL) {
175✔
450
                return NULL;
451
        }
452

453
        _cleanup_agent_ Agent *agent = malloc0(sizeof(Agent));
175✔
454
        agent->ref_count = 1;
175✔
455
        agent->event = steal_pointer(&event);
175✔
456
        agent->api_bus_service_name = steal_pointer(&service_name);
175✔
457
        agent->peer_socket_options = steal_pointer(&socket_opts);
175✔
458
        agent->unit_infos = unit_infos;
175✔
459
        agent->connection_state = AGENT_CONNECTION_STATE_DISCONNECTED;
175✔
460
        agent->connection_retry_count = 0;
175✔
461
        agent->controller_last_seen = 0;
175✔
462
        agent->controller_last_seen_monotonic = 0;
175✔
463
        agent->wildcard_subscription_active = false;
175✔
464
        agent->metrics_enabled = false;
175✔
465
        agent->disconnect_timestamp = 0;
175✔
466
        agent->disconnect_timestamp_monotonic = 0;
175✔
467
        LIST_HEAD_INIT(agent->outstanding_requests);
175✔
468
        LIST_HEAD_INIT(agent->tracked_jobs);
175✔
469
        LIST_HEAD_INIT(agent->proxy_services);
175✔
470

471
        return steal_pointer(&agent);
175✔
472
}
473

474
Agent *agent_ref(Agent *agent) {
216✔
475
        assert(agent->ref_count > 0);
216✔
476

477
        agent->ref_count++;
216✔
478
        return agent;
216✔
479
}
480

481
/* Emit == false on agent shutdown or proxy creation error */
482
void agent_remove_proxy(Agent *agent, ProxyService *proxy, bool emit_removed) {
13✔
483
        assert(proxy->agent == agent);
13✔
484

485
        bc_log_debugf("Removing proxy %s %s", proxy->node_name, proxy->unit_name);
13✔
486

487
        if (emit_removed) {
13✔
488
                int r = proxy_service_emit_proxy_removed(proxy);
11✔
489
                if (r < 0) {
11✔
490
                        bc_log_errorf("Failed to emit ProxyRemoved signal for node %s and unit %s",
×
491
                                      proxy->node_name,
492
                                      proxy->unit_name);
493
                }
494
        }
495

496
        if (proxy->request_message != NULL) {
13✔
497
                int r = sd_bus_reply_method_errorf(
×
498
                                proxy->request_message, SD_BUS_ERROR_FAILED, "Proxy service stopped");
499
                if (r < 0) {
×
500
                        bc_log_errorf("Failed to sent ready status message to proxy: %s", strerror(-r));
×
501
                }
502
                sd_bus_message_unrefp(&proxy->request_message);
×
503
                proxy->request_message = NULL;
×
504
        }
505

506

507
        if (proxy->sent_successful_ready) {
13✔
508
                int r = agent_stop_local_proxy_service(agent, proxy);
10✔
509
                if (r < 0) {
10✔
510
                        bc_log_errorf("Failed to stop proxy service: %s", strerror(-r));
×
511
                }
512
        }
513

514
        proxy_service_unexport(proxy);
13✔
515

516
        LIST_REMOVE(proxy_services, agent->proxy_services, proxy);
13✔
517
        proxy->agent = NULL;
13✔
518
        proxy_service_unref(proxy);
13✔
519
}
13✔
520

521
void agent_unref(Agent *agent) {
384✔
522
        assert(agent->ref_count > 0);
384✔
523

524
        agent->ref_count--;
384✔
525
        if (agent->ref_count != 0) {
384✔
526
                return;
527
        }
528

529
        bc_log_debug("Finalizing agent");
169✔
530

531
        /* These are removed in agent_stop */
532
        assert(LIST_IS_EMPTY(agent->proxy_services));
169✔
533

534
        hashmap_free(agent->unit_infos);
169✔
535

536
        free_and_null(agent->name);
169✔
537
        free_and_null(agent->host);
169✔
538
        free_and_null(agent->assembled_controller_address);
169✔
539
        free_and_null(agent->api_bus_service_name);
169✔
540
        free_and_null(agent->controller_address);
169✔
541
        free_and_null(agent->peer_socket_options);
169✔
542

543
        if (agent->event != NULL) {
169✔
544
                sd_event_unrefp(&agent->event);
169✔
545
        }
546

547
        if (agent->register_call_slot != NULL) {
169✔
548
                sd_bus_slot_unrefp(&agent->register_call_slot);
×
549
        }
550
        if (agent->metrics_slot != NULL) {
169✔
551
                sd_bus_slot_unrefp(&agent->metrics_slot);
2✔
552
        }
553

554
        if (agent->peer_dbus != NULL) {
169✔
555
                sd_bus_unrefp(&agent->peer_dbus);
×
556
        }
557
        if (agent->api_bus != NULL) {
169✔
558
                sd_bus_unrefp(&agent->api_bus);
144✔
559
        }
560
        if (agent->systemd_dbus != NULL) {
169✔
561
                sd_bus_unrefp(&agent->systemd_dbus);
144✔
562
        }
563

564
        if (agent->config) {
169✔
565
                cfg_dispose(agent->config);
169✔
566
                agent->config = NULL;
169✔
567
        }
568
        free(agent);
169✔
569
}
570

571
bool agent_set_port(Agent *agent, const char *port_s) {
154✔
572
        uint16_t port = 0;
154✔
573

574
        if (!parse_port(port_s, &port)) {
154✔
575
                bc_log_errorf("Invalid port format '%s'", port_s);
5✔
576
                return false;
5✔
577
        }
578
        agent->port = port;
149✔
579
        return true;
149✔
580
}
581

582
bool agent_set_controller_address(Agent *agent, const char *address) {
8✔
583
        return copy_str(&agent->controller_address, address);
8✔
584
}
585

586
bool agent_set_assembled_controller_address(Agent *agent, const char *address) {
175✔
587
        return copy_str(&agent->assembled_controller_address, address);
175✔
588
}
589

590
bool agent_set_host(Agent *agent, const char *host) {
149✔
591
        return copy_str(&agent->host, host);
149✔
592
}
593

594
bool agent_set_name(Agent *agent, const char *name) {
156✔
595
        return copy_str(&agent->name, name);
156✔
596
}
597

598
bool agent_set_heartbeat_interval(Agent *agent, const char *interval_msec) {
151✔
599
        long interval = 0;
151✔
600

601
        if (!parse_long(interval_msec, &interval)) {
151✔
602
                bc_log_errorf("Invalid heartbeat interval format '%s'", interval_msec);
×
603
                return false;
×
604
        }
605
        agent->heartbeat_interval_msec = interval;
151✔
606
        return true;
151✔
607
}
608

609
bool agent_set_controller_heartbeat_threshold(Agent *agent, const char *threshold_msec) {
150✔
610
        long threshold = 0;
150✔
611

612
        if (!parse_long(threshold_msec, &threshold)) {
150✔
613
                bc_log_errorf("Invalid heartbeat threshold format '%s'", threshold_msec);
×
614
                return false;
×
615
        }
616
        agent->controller_heartbeat_threshold_msec = threshold;
150✔
617
        return true;
150✔
618
}
619

620
void agent_set_systemd_user(Agent *agent, bool systemd_user) {
150✔
621
        agent->systemd_user = systemd_user;
150✔
622
}
150✔
623

624
bool agent_parse_config(Agent *agent, const char *configfile) {
175✔
625
        int result = 0;
175✔
626

627
        result = cfg_initialize(&agent->config);
175✔
628
        if (result != 0) {
175✔
629
                fprintf(stderr, "Error initializing configuration: '%s'.\n", strerror(-result));
×
630
                return false;
×
631
        }
632

633
        result = cfg_agent_def_conf(agent->config);
175✔
634
        if (result < 0) {
175✔
635
                fprintf(stderr, "Failed to set default settings for agent: %s", strerror(-result));
×
636
                return false;
×
637
        }
638
        result = cfg_load_complete_configuration(
175✔
639
                        agent->config,
640
                        CFG_AGENT_DEFAULT_CONFIG,
641
                        CFG_ETC_BC_AGENT_CONF,
642
                        CFG_ETC_AGENT_CONF_DIR,
643
                        configfile);
644
        if (result != 0) {
175✔
645
                return false;
20✔
646
        }
647
        return true;
648
}
649

650
bool agent_apply_config(Agent *agent) {
155✔
651
        const char *value = NULL;
155✔
652
        value = cfg_get_value(agent->config, CFG_NODE_NAME);
155✔
653
        if (value) {
155✔
654
                if (!agent_set_name(agent, value)) {
155✔
655
                        bc_log_error("Failed to set CONTROLLER NAME");
×
656
                        return false;
×
657
                }
658
        }
659

660
        value = cfg_get_value(agent->config, CFG_CONTROLLER_HOST);
155✔
661
        if (value) {
155✔
662
                if (!agent_set_host(agent, value)) {
148✔
663
                        bc_log_error("Failed to set CONTROLLER HOST");
×
664
                        return false;
×
665
                }
666
        }
667

668
        value = cfg_get_value(agent->config, CFG_CONTROLLER_PORT);
155✔
669
        if (value) {
155✔
670
                if (!agent_set_port(agent, value)) {
152✔
671
                        return false;
672
                }
673
        }
674

675
        value = cfg_get_value(agent->config, CFG_CONTROLLER_ADDRESS);
150✔
676
        if (value) {
150✔
677
                if (!agent_set_controller_address(agent, value)) {
7✔
678
                        bc_log_error("Failed to set CONTROLLER ADDRESS");
×
679
                        return false;
×
680
                }
681
        }
682

683
        value = cfg_get_value(agent->config, CFG_HEARTBEAT_INTERVAL);
150✔
684
        if (value) {
150✔
685
                if (!agent_set_heartbeat_interval(agent, value)) {
150✔
686
                        return false;
687
                }
688
        }
689

690
        value = cfg_get_value(agent->config, CFG_CONTROLLER_HEARTBEAT_THRESHOLD);
150✔
691
        if (value) {
150✔
692
                if (!agent_set_controller_heartbeat_threshold(agent, value)) {
150✔
693
                        return false;
694
                }
695
        }
696

697
        /* Set socket options used for peer connections with the agents */
698
        const char *keepidle = cfg_get_value(agent->config, CFG_TCP_KEEPALIVE_TIME);
150✔
699
        if (keepidle) {
150✔
700
                if (socket_options_set_tcp_keepidle(agent->peer_socket_options, keepidle) < 0) {
150✔
701
                        bc_log_error("Failed to set TCP KEEPIDLE");
×
702
                        return false;
×
703
                }
704
        }
705
        const char *keepintvl = cfg_get_value(agent->config, CFG_TCP_KEEPALIVE_INTERVAL);
150✔
706
        if (keepintvl) {
150✔
707
                if (socket_options_set_tcp_keepintvl(agent->peer_socket_options, keepintvl) < 0) {
150✔
708
                        bc_log_error("Failed to set TCP KEEPINTVL");
×
709
                        return false;
×
710
                }
711
        }
712
        const char *keepcnt = cfg_get_value(agent->config, CFG_TCP_KEEPALIVE_COUNT);
150✔
713
        if (keepcnt) {
150✔
714
                if (socket_options_set_tcp_keepcnt(agent->peer_socket_options, keepcnt) < 0) {
150✔
715
                        bc_log_error("Failed to set TCP KEEPCNT");
×
716
                        return false;
×
717
                }
718
        }
719
        if (socket_options_set_ip_recverr(
150✔
720
                            agent->peer_socket_options,
721
                            cfg_get_bool_value(agent->config, CFG_IP_RECEIVE_ERRORS)) < 0) {
150✔
722
                bc_log_error("Failed to set IP RECVERR");
×
723
                return false;
×
724
        }
725

726
        return true;
727
}
728

729
static void agent_update_unit_infos_for(Agent *agent, AgentUnitInfo *info) {
9,202✔
730
        if (!info->subscribed && !info->loaded) {
9,202✔
731
                AgentUnitInfoKey key = { info->object_path };
9,175✔
732
                AgentUnitInfo *info = (AgentUnitInfo *) hashmap_delete(agent->unit_infos, &key);
9,175✔
733
                if (info != NULL) {
9,175✔
734
                        unit_info_clear(info);
9,175✔
735
                }
736
        }
737
}
9,202✔
738

739
static AgentUnitInfo *agent_get_unit_info(Agent *agent, const char *unit_path) {
64,204✔
740
        AgentUnitInfoKey key = { (char *) unit_path };
64,204✔
741

742
        return (AgentUnitInfo *) hashmap_get(agent->unit_infos, &key);
64,204✔
743
}
744

745
static AgentUnitInfo *agent_ensure_unit_info(Agent *agent, const char *unit) {
26,364✔
746
        _cleanup_free_ char *unit_path = make_unit_path(unit);
52,728✔
747
        AgentUnitInfo *info = agent_get_unit_info(agent, unit_path);
26,364✔
748
        if (info != NULL) {
26,364✔
749
                return info;
750
        }
751

752
        _cleanup_free_ char *unit_copy = strdup(unit);
26,364✔
753
        if (unit_copy == NULL) {
26,337✔
754
                return NULL;
755
        }
756

757
        AgentUnitInfo v = { unit_path, unit_copy, false, false, -1, NULL };
26,337✔
758

759
        AgentUnitInfo *replaced = (AgentUnitInfo *) hashmap_set(agent->unit_infos, &v);
26,337✔
760
        if (replaced == NULL && hashmap_oom(agent->unit_infos)) {
26,337✔
761
                return NULL;
762
        }
763

764
        info = agent_get_unit_info(agent, unit_path);
26,337✔
765

766
        /* These are now in hashtable */
767
        steal_pointer(&unit_copy);
768
        steal_pointer(&unit_path);
769

770
        return info;
771
}
772

773
static int agent_method_passthrough_to_systemd_cb(
25✔
774
                sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
775
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
25✔
776

777
        if (sd_bus_message_is_method_error(m, NULL)) {
25✔
778
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
2✔
779
        }
780

781
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
25✔
782
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
23✔
783
        if (r < 0) {
23✔
784
                return r;
785
        }
786

787
        r = sd_bus_message_copy(reply, m, true);
23✔
788
        if (r < 0) {
23✔
789
                return r;
790
        }
791

792
        return sd_bus_message_send(reply);
23✔
793
}
794

795
static int agent_method_passthrough_to_systemd(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
25✔
796
        Agent *agent = userdata;
25✔
797
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(
50✔
798
                        agent, m, sd_bus_message_get_member(m));
799
        if (req == NULL) {
25✔
800
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to create a systemd request");
×
801
        }
802

803
        int r = sd_bus_message_copy(req->message, m, true);
25✔
804
        if (r < 0) {
25✔
805
                return sd_bus_reply_method_errorf(
×
806
                                m, SD_BUS_ERROR_FAILED, "Failed to copy a reply message: %s", strerror(-r));
807
        }
808

809
        if (!systemd_request_start(req, agent_method_passthrough_to_systemd_cb)) {
25✔
810
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
811
        }
812

813
        return 1;
814
}
815

816
/************************************************************************
817
 ********** org.eclipse.bluechi.internal.Agent.ListUnits **
818
 ************************************************************************/
819

820
static int list_units_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
4✔
821
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
4✔
822

823
        if (sd_bus_message_is_method_error(m, NULL)) {
4✔
824
                /* Forward error */
825
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
826
        }
827

828
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
4✔
829

830
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
4✔
831
        if (r < 0) {
4✔
832
                return r;
833
        }
834

835
        r = sd_bus_message_copy(reply, m, true);
4✔
836
        if (r < 0) {
4✔
837
                return r;
838
        }
839

840
        return sd_bus_message_send(reply);
4✔
841
}
842

843
static int agent_method_list_units(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
4✔
844
        Agent *agent = userdata;
4✔
845

846
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(agent, m, "ListUnits");
8✔
847
        if (req == NULL) {
4✔
848
                return sd_bus_reply_method_errorf(
×
849
                                m,
850
                                SD_BUS_ERROR_FAILED,
851
                                "Failed to create a systemd request for the ListUnits method");
852
        }
853

854
        if (!systemd_request_start(req, list_units_callback)) {
4✔
855
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
856
        }
857

858
        return 1;
859
}
860

861
/************************************************************************
862
 ********** org.eclipse.bluechi.internal.Agent.ListUnitFiles ************
863
 ************************************************************************/
864

865
static int list_unit_files_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
4✔
866
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
4✔
867

868
        if (sd_bus_message_is_method_error(m, NULL)) {
4✔
869
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
870
        }
871

872
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
4✔
873

874
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
4✔
875
        if (r < 0) {
4✔
876
                return r;
877
        }
878

879
        r = sd_bus_message_copy(reply, m, true);
4✔
880
        if (r < 0) {
4✔
881
                return r;
882
        }
883

884
        return sd_bus_message_send(reply);
4✔
885
}
886

887
static int agent_method_list_unit_files(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
4✔
888
        Agent *agent = userdata;
4✔
889

890
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(agent, m, "ListUnitFiles");
8✔
891
        if (req == NULL) {
4✔
892
                return sd_bus_reply_method_errorf(
×
893
                                m,
894
                                SD_BUS_ERROR_FAILED,
895
                                "Failed to create a systemd request for the ListUnitFiles method");
896
        }
897

898
        if (!systemd_request_start(req, list_unit_files_callback)) {
4✔
899
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
900
        }
901

902
        return 1;
903
}
904

905
/************************************************************************
906
 ******** org.eclipse.bluechi.internal.Agent.GetUnitProperties ************
907
 ************************************************************************/
908

909
static int get_unit_properties_got_properties(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
6✔
910
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
6✔
911

912
        if (sd_bus_message_is_method_error(m, NULL)) {
6✔
913
                /* Forward error */
914
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
915
        }
916

917
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
6✔
918
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
6✔
919
        if (r < 0) {
6✔
920
                return r;
921
        }
922

923
        r = sd_bus_message_copy(reply, m, true);
6✔
924
        if (r < 0) {
6✔
925
                return r;
926
        }
927

928
        return sd_bus_message_send(reply);
6✔
929
}
930

931
static int agent_method_get_unit_properties(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
6✔
932
        Agent *agent = userdata;
6✔
933
        const char *interface = NULL;
6✔
934
        const char *unit = NULL;
6✔
935
        _cleanup_free_ char *unit_path = NULL;
6✔
936

937
        int r = sd_bus_message_read(m, "ss", &unit, &interface);
6✔
938
        if (r < 0) {
6✔
939
                return sd_bus_reply_method_errorf(
×
940
                                m,
941
                                SD_BUS_ERROR_FAILED,
942
                                "Failed to read a message containing unit and interface: %s",
943
                                strerror(-r));
944
        }
945

946
        r = assemble_object_path_string(SYSTEMD_OBJECT_PATH "/unit", unit, &unit_path);
6✔
947
        if (r < 0) {
6✔
948
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "OOM when assembling unit path");
×
949
        }
950

951
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request_full(
12✔
952
                        agent, m, unit_path, "org.freedesktop.DBus.Properties", "GetAll");
953
        if (req == NULL) {
6✔
954
                return sd_bus_reply_method_errorf(
×
955
                                m,
956
                                SD_BUS_ERROR_FAILED,
957
                                "Failed to create a systemd request for the GetAll method");
958
        }
959

960
        r = sd_bus_message_append(req->message, "s", interface);
6✔
961
        if (r < 0) {
6✔
962
                return sd_bus_reply_method_errorf(
×
963
                                m,
964
                                SD_BUS_ERROR_FAILED,
965
                                "Failed to append the interface to the message: %s",
966
                                strerror(-r));
967
        }
968

969
        if (!systemd_request_start(req, get_unit_properties_got_properties)) {
6✔
970
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
971
        }
972

973
        return 1;
974
}
975

976
/***************************************************************************
977
 ******** org.eclipse.bluechi.internal.Agent.GetUnitProperty *
978
 ***************************************************************************/
979

980
static int get_unit_property_got_property(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
11✔
981
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
11✔
982

983
        if (sd_bus_message_is_method_error(m, NULL)) {
11✔
984
                /* Forward error */
985
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
986
        }
987

988
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
11✔
989
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
11✔
990
        if (r < 0) {
11✔
991
                return r;
992
        }
993

994
        r = sd_bus_message_copy(reply, m, true);
11✔
995
        if (r < 0) {
11✔
996
                return r;
997
        }
998

999
        return sd_bus_message_send(reply);
11✔
1000
}
1001

1002
static int agent_method_get_unit_property(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
11✔
1003
        Agent *agent = userdata;
11✔
1004
        const char *interface = NULL;
11✔
1005
        const char *unit = NULL;
11✔
1006
        const char *property = NULL;
11✔
1007
        _cleanup_free_ char *unit_path = NULL;
11✔
1008

1009
        int r = sd_bus_message_read(m, "sss", &unit, &interface, &property);
11✔
1010
        if (r < 0) {
11✔
1011
                return sd_bus_reply_method_errorf(
×
1012
                                m,
1013
                                SD_BUS_ERROR_FAILED,
1014
                                "Failed to read a message containing unit, interface, and property: %s",
1015
                                strerror(-r));
1016
        }
1017

1018
        r = assemble_object_path_string(SYSTEMD_OBJECT_PATH "/unit", unit, &unit_path);
11✔
1019
        if (r < 0) {
11✔
1020
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "Out of memory");
×
1021
        }
1022

1023
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request_full(
22✔
1024
                        agent, m, unit_path, "org.freedesktop.DBus.Properties", "Get");
1025
        if (req == NULL) {
11✔
1026
                return sd_bus_reply_method_errorf(
×
1027
                                m, SD_BUS_ERROR_FAILED, "Failed to create a systemd request for the Get method");
1028
        }
1029

1030
        r = sd_bus_message_append(req->message, "ss", interface, property);
11✔
1031
        if (r < 0) {
11✔
1032
                return sd_bus_reply_method_errorf(
×
1033
                                m,
1034
                                SD_BUS_ERROR_FAILED,
1035
                                "Failed to append interface and property to the message: %s",
1036
                                strerror(-r));
1037
        }
1038

1039
        if (!systemd_request_start(req, get_unit_property_got_property)) {
11✔
1040
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
1041
        }
1042

1043
        return 1;
1044
}
1045

1046
/******************************************************************************
1047
 ******** org.eclipse.bluechi.internal.Agent.SetUnitProperties  *
1048
 ******************************************************************************/
1049

1050
static int set_unit_properties_cb(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1051
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
1✔
1052

1053
        if (sd_bus_message_is_method_error(m, NULL)) {
1✔
1054
                /* Forward error */
1055
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
1056
        }
1057

1058
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
1✔
1059
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
1✔
1060
        if (r < 0) {
1✔
1061
                return r;
1062
        }
1063

1064
        r = sd_bus_message_copy(reply, m, true);
1✔
1065
        if (r < 0) {
1✔
1066
                return r;
1067
        }
1068

1069
        return sd_bus_message_send(reply);
1✔
1070
}
1071

1072
static int agent_method_set_unit_properties(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1073
        Agent *agent = userdata;
1✔
1074
        const char *unit = NULL;
1✔
1075
        int runtime = 0;
1✔
1076
        _cleanup_free_ char *unit_path = NULL;
1✔
1077

1078
        int r = sd_bus_message_read(m, "sb", &unit, &runtime);
1✔
1079
        if (r < 0) {
1✔
1080
                return sd_bus_reply_method_errorf(
×
1081
                                m,
1082
                                SD_BUS_ERROR_FAILED,
1083
                                "Failed to read a message containing unit and runtime: %s",
1084
                                strerror(-r));
1085
        }
1086

1087
        r = assemble_object_path_string(SYSTEMD_OBJECT_PATH "/unit", unit, &unit_path);
1✔
1088
        if (r < 0) {
1✔
1089
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "Out of memory");
×
1090
        }
1091

1092
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request_full(
2✔
1093
                        agent, m, unit_path, "org.freedesktop.systemd1.Unit", "SetProperties");
1094
        if (req == NULL) {
1✔
1095
                return sd_bus_reply_method_errorf(
×
1096
                                m,
1097
                                SD_BUS_ERROR_FAILED,
1098
                                "Failed to create a systemd request for the SetProperties method");
1099
        }
1100

1101
        r = sd_bus_message_append(req->message, "b", runtime);
1✔
1102
        if (r < 0) {
1✔
1103
                return sd_bus_reply_method_errorf(
×
1104
                                m,
1105
                                SD_BUS_ERROR_FAILED,
1106
                                "Failed to append a runtime to a message: %s",
1107
                                strerror(-r));
1108
        }
1109

1110
        r = sd_bus_message_copy(req->message, m, false);
1✔
1111
        if (r < 0) {
1✔
1112
                return sd_bus_reply_method_errorf(
×
1113
                                m, SD_BUS_ERROR_FAILED, "Failed to copy a message: %s", strerror(-r));
1114
        }
1115

1116
        if (!systemd_request_start(req, set_unit_properties_cb)) {
1✔
1117
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start a systemd request");
×
1118
        }
1119

1120
        return 1;
1121
}
1122

1123
/******************************************************************************
1124
 ******** org.eclipse.bluechi.internal.Agent.FreezeUnit  *
1125
 ******************************************************************************/
1126

1127
static int freeze_unit_cb(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1128
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
1✔
1129

1130
        if (sd_bus_message_is_method_error(m, NULL)) {
1✔
1131
                /* Forward error */
1132
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
1133
        }
1134

1135
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
1✔
1136
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
1✔
1137
        if (r < 0) {
1✔
1138
                return r;
1139
        }
1140

1141
        r = sd_bus_message_copy(reply, m, true);
1✔
1142
        if (r < 0) {
1✔
1143
                return r;
1144
        }
1145

1146
        return sd_bus_message_send(reply);
1✔
1147
}
1148

1149
static int agent_method_freeze_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1150
        Agent *agent = userdata;
1✔
1151
        const char *unit = NULL;
1✔
1152
        _cleanup_free_ char *unit_path = NULL;
1✔
1153

1154
        int r = sd_bus_message_read(m, "s", &unit);
1✔
1155
        if (r < 0) {
1✔
1156
                return sd_bus_reply_method_errorf(
×
1157
                                m, SD_BUS_ERROR_INVALID_ARGS, "Invalid argument for the unit");
1158
        }
1159

1160
        r = assemble_object_path_string(SYSTEMD_OBJECT_PATH "/unit", unit, &unit_path);
1✔
1161
        if (r < 0) {
1✔
1162
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "Out of memory");
×
1163
        }
1164

1165
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request_full(
2✔
1166
                        agent, m, unit_path, "org.freedesktop.systemd1.Unit", "Freeze");
1167
        if (req == NULL) {
1✔
1168
                return sd_bus_reply_method_errorf(
×
1169
                                m,
1170
                                SD_BUS_ERROR_FAILED,
1171
                                "Failed to create a systemd request for the Freeze method");
1172
        }
1173

1174
        if (!systemd_request_start(req, freeze_unit_cb)) {
1✔
1175
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
1176
        }
1177

1178
        return 1;
1179
}
1180

1181
/******************************************************************************
1182
 ******** org.eclipse.bluechi.internal.Agent.ThawUnit  *
1183
 ******************************************************************************/
1184

1185
static int thaw_unit_cb(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1186
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
1✔
1187

1188
        if (sd_bus_message_is_method_error(m, NULL)) {
1✔
1189
                /* Forward error */
1190
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
1191
        }
1192

1193
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
1✔
1194
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
1✔
1195
        if (r < 0) {
1✔
1196
                return r;
1197
        }
1198

1199
        r = sd_bus_message_copy(reply, m, true);
1✔
1200
        if (r < 0) {
1✔
1201
                return r;
1202
        }
1203

1204
        return sd_bus_message_send(reply);
1✔
1205
}
1206

1207
static int agent_method_thaw_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1208
        Agent *agent = userdata;
1✔
1209
        const char *unit = NULL;
1✔
1210
        _cleanup_free_ char *unit_path = NULL;
1✔
1211

1212
        int r = sd_bus_message_read(m, "s", &unit);
1✔
1213
        if (r < 0) {
1✔
1214
                return sd_bus_reply_method_errorf(
×
1215
                                m, SD_BUS_ERROR_INVALID_ARGS, "Invalid argument for the unit");
1216
        }
1217

1218
        r = assemble_object_path_string(SYSTEMD_OBJECT_PATH "/unit", unit, &unit_path);
1✔
1219
        if (r < 0) {
1✔
1220
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_NO_MEMORY, "Out of memory");
×
1221
        }
1222

1223
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request_full(
2✔
1224
                        agent, m, unit_path, "org.freedesktop.systemd1.Unit", "Thaw");
1225
        if (req == NULL) {
1✔
1226
                return sd_bus_reply_method_errorf(
×
1227
                                m,
1228
                                SD_BUS_ERROR_FAILED,
1229
                                "Failed to create a systemd request for the Thaw method");
1230
        }
1231

1232
        if (!systemd_request_start(req, thaw_unit_cb)) {
1✔
1233
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start a systemd request");
×
1234
        }
1235

1236
        return 1;
1237
}
1238

1239
static void agent_job_done(UNUSED sd_bus_message *m, const char *result, void *userdata) {
63✔
1240
        AgentJobOp *op = userdata;
63✔
1241
        Agent *agent = op->agent;
63✔
1242

1243
        if (agent->metrics_enabled) {
63✔
1244
                agent_send_job_metrics(
5✔
1245
                                agent,
1246
                                op->unit,
1247
                                op->method,
1248
                                // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
1249
                                finalize_time_interval_micros(op->job_start_micros));
5✔
1250
        }
1251

1252
        bc_log_infof("Sending JobDone %u, result: %s", op->bc_job_id, result);
63✔
1253

1254
        int r = sd_bus_emit_signal(
63✔
1255
                        agent->peer_dbus,
1256
                        INTERNAL_AGENT_OBJECT_PATH,
1257
                        INTERNAL_AGENT_INTERFACE,
1258
                        "JobDone",
1259
                        "us",
1260
                        op->bc_job_id,
1261
                        result);
1262
        if (r < 0) {
63✔
1263
                bc_log_errorf("Failed to emit JobDone: %s", strerror(-r));
1✔
1264
        }
1265
}
63✔
1266

1267
static int unit_lifecycle_method_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
66✔
1268
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
66✔
1269
        Agent *agent = req->agent;
66✔
1270
        const char *job_object_path = NULL;
66✔
1271

1272
        if (sd_bus_message_is_method_error(m, NULL)) {
66✔
1273
                /* Forward error */
1274
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
1275
        }
1276

1277
        int r = sd_bus_message_read(m, "o", &job_object_path);
66✔
1278
        if (r < 0) {
66✔
1279
                return sd_bus_reply_method_errorf(
×
1280
                                req->request_message,
1281
                                SD_BUS_ERROR_FAILED,
1282
                                "Failed to read a message containing the object path of the job: %s",
1283
                                strerror(-r));
1284
        }
1285

1286
        AgentJobOp *op = req->userdata;
66✔
1287

1288
        if (!agent_track_job(
66✔
1289
                            agent,
1290
                            job_object_path,
1291
                            agent_job_done,
1292
                            agent_job_op_ref(op),
66✔
1293
                            (free_func_t) agent_job_op_unref)) {
1294
                return sd_bus_reply_method_errorf(
×
1295
                                req->request_message, SD_BUS_ERROR_FAILED, "Failed to track a job");
1296
        }
1297

1298
        return sd_bus_reply_method_return(req->request_message, "");
66✔
1299
}
1300

1301
static int agent_run_unit_lifecycle_method(sd_bus_message *m, Agent *agent, const char *method) {
67✔
1302
        const char *name = NULL;
67✔
1303
        const char *mode = NULL;
67✔
1304
        uint32_t job_id = 0;
67✔
1305

1306
        int r = sd_bus_message_read(m, "ssu", &name, &mode, &job_id);
67✔
1307
        if (r < 0) {
67✔
1308
                return sd_bus_reply_method_errorf(
×
1309
                                m,
1310
                                SD_BUS_ERROR_INVALID_ARGS,
1311
                                "Failed to read a message containing name, mode, and job ID: %s",
1312
                                strerror(-r));
1313
        }
1314

1315
        bc_log_infof("Request to %s unit: %s - Action: %s", method, name, mode);
67✔
1316

1317
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(agent, m, method);
134✔
1318
        if (req == NULL) {
67✔
1319
                return sd_bus_reply_method_errorf(
×
1320
                                m,
1321
                                SD_BUS_ERROR_FAILED,
1322
                                "Failed to create a systemd request for %s method: %s",
1323
                                method,
1324
                                strerror(-r));
1325
        }
1326

1327
        _cleanup_agent_job_op_ AgentJobOp *op = agent_job_new(agent, job_id, name, method);
134✔
1328
        if (op == NULL) {
67✔
1329
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to create JobOp");
×
1330
        }
1331

1332
        systemd_request_set_userdata(req, agent_job_op_ref(op), (free_func_t) agent_job_op_unref);
67✔
1333

1334
        r = sd_bus_message_append(req->message, "ss", name, mode);
67✔
1335
        if (r < 0) {
67✔
1336
                return sd_bus_reply_method_errorf(
×
1337
                                m,
1338
                                SD_BUS_ERROR_FAILED,
1339
                                "Failed to append name and mode to the message: %s",
1340
                                strerror(-r));
1341
        }
1342

1343
        if (!systemd_request_start(req, unit_lifecycle_method_callback)) {
67✔
1344
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start a systemd request");
×
1345
        }
1346

1347
        return 1;
1348
}
1349

1350
/*************************************************************************
1351
 ********** org.eclipse.bluechi.internal.Agent.StartUnit   *
1352
 *************************************************************************/
1353

1354
static int agent_method_start_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
48✔
1355
        return agent_run_unit_lifecycle_method(m, (Agent *) userdata, "StartUnit");
48✔
1356
}
1357

1358
/*************************************************************************
1359
 ********** org.eclipse.bluechi.internal.Agent.StopUnit    *
1360
 *************************************************************************/
1361

1362
static int agent_method_stop_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
16✔
1363
        return agent_run_unit_lifecycle_method(m, (Agent *) userdata, "StopUnit");
16✔
1364
}
1365

1366
/*************************************************************************
1367
 ********** org.eclipse.bluechi.internal.Agent.RestartUnit *
1368
 *************************************************************************/
1369

1370
static int agent_method_restart_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2✔
1371
        return agent_run_unit_lifecycle_method(m, (Agent *) userdata, "RestartUnit");
2✔
1372
}
1373

1374
/*************************************************************************
1375
 ********** org.eclipse.bluechi.internal.Agent.ReloadUnit  *
1376
 *************************************************************************/
1377

1378
static int agent_method_reload_unit(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1379
        return agent_run_unit_lifecycle_method(m, (Agent *) userdata, "ReloadUnit");
1✔
1380
}
1381

1382
/*************************************************************************
1383
 ********** org.eclipse.bluechi.internal.Agent.Subscribe ***
1384
 *************************************************************************/
1385

1386
static void agent_emit_unit_new(Agent *agent, AgentUnitInfo *info, const char *reason) {
35✔
1387
        bc_log_debugf("Sending UnitNew %s, reason: %s", info->unit, reason);
35✔
1388
        int r = sd_bus_emit_signal(
35✔
1389
                        agent->peer_dbus,
1390
                        INTERNAL_AGENT_OBJECT_PATH,
1391
                        INTERNAL_AGENT_INTERFACE,
1392
                        "UnitNew",
1393
                        "ss",
1394
                        info->unit,
1395
                        reason);
1396
        if (r < 0) {
35✔
1397
                bc_log_warn("Failed to emit UnitNew");
×
1398
        }
1399
}
35✔
1400

1401
static void agent_emit_unit_removed(Agent *agent, AgentUnitInfo *info) {
19✔
1402
        bc_log_debugf("Sending UnitRemoved %s", info->unit);
19✔
1403

1404
        int r = sd_bus_emit_signal(
19✔
1405
                        agent->peer_dbus,
1406
                        INTERNAL_AGENT_OBJECT_PATH,
1407
                        INTERNAL_AGENT_INTERFACE,
1408
                        "UnitRemoved",
1409
                        "s",
1410
                        info->unit);
1411
        if (r < 0) {
19✔
1412
                bc_log_warn("Failed to emit UnitRemoved");
×
1413
        }
1414
}
19✔
1415

1416
static void agent_emit_unit_state_changed(Agent *agent, AgentUnitInfo *info, const char *reason) {
50✔
1417
        bc_log_debugf("Sending UnitStateChanged %s %s(%s) reason: %s",
50✔
1418
                      info->unit,
1419
                      active_state_to_string(info->active_state),
1420
                      unit_info_get_substate(info),
1421
                      reason);
1422
        int r = sd_bus_emit_signal(
50✔
1423
                        agent->peer_dbus,
1424
                        INTERNAL_AGENT_OBJECT_PATH,
1425
                        INTERNAL_AGENT_INTERFACE,
1426
                        "UnitStateChanged",
1427
                        "ssss",
1428
                        info->unit,
1429
                        active_state_to_string(info->active_state),
1430
                        unit_info_get_substate(info),
1431
                        reason);
1432
        if (r < 0) {
50✔
1433
                bc_log_warn("Failed to emit UnitStateChanged");
×
1434
        }
1435
}
50✔
1436

1437
static int agent_method_subscribe(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
30✔
1438
        Agent *agent = userdata;
30✔
1439
        const char *unit = NULL;
30✔
1440
        int r = sd_bus_message_read(m, "s", &unit);
30✔
1441
        if (r < 0) {
30✔
1442
                return sd_bus_reply_method_errorf(
×
1443
                                m, SD_BUS_ERROR_INVALID_ARGS, "Invalid argument for the unit: %s", strerror(-r));
1444
        }
1445

1446
        if (is_wildcard(unit)) {
30✔
1447
                if (agent->wildcard_subscription_active) {
6✔
1448
                        return sd_bus_reply_method_errorf(
6✔
1449
                                        m, SD_BUS_ERROR_FAILED, "Already wildcard subscribed");
1450
                }
1451
                agent->wildcard_subscription_active = true;
6✔
1452

1453
                AgentUnitInfo info = { NULL, (char *) unit, true, true, _UNIT_ACTIVE_STATE_INVALID, NULL };
6✔
1454
                agent_emit_unit_new(agent, &info, "virtual");
6✔
1455

1456
                return sd_bus_reply_method_return(m, "");
6✔
1457
        }
1458

1459
        AgentUnitInfo *info = agent_ensure_unit_info(agent, unit);
24✔
1460
        if (info == NULL) {
24✔
1461
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to ensure the unit info");
×
1462
        }
1463

1464
        if (info->subscribed) {
24✔
1465
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Already subscribed");
×
1466
        }
1467

1468
        info->subscribed = true;
24✔
1469

1470
        if (info->loaded) {
24✔
1471
                /* The unit was already loaded, synthesize a UnitNew */
1472
                agent_emit_unit_new(agent, info, "virtual");
1✔
1473

1474
                if (info->active_state != _UNIT_ACTIVE_STATE_INVALID) {
1✔
1475
                        agent_emit_unit_state_changed(agent, info, "virtual");
1✔
1476
                }
1477
        }
1478

1479
        return sd_bus_reply_method_return(m, "");
24✔
1480
}
1481

1482
/*************************************************************************
1483
 *** org.eclipse.bluechi.internal.Agent.Unsubscribe ********
1484
 *************************************************************************/
1485

1486
static int agent_method_unsubscribe(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
29✔
1487
        Agent *agent = userdata;
29✔
1488
        const char *unit = NULL;
29✔
1489
        int r = sd_bus_message_read(m, "s", &unit);
29✔
1490
        if (r < 0) {
29✔
1491
                return sd_bus_reply_method_errorf(
×
1492
                                m, SD_BUS_ERROR_INVALID_ARGS, "Invalid argument for the unit: %s", strerror(-r));
1493
        }
1494

1495
        if (is_wildcard(unit)) {
29✔
1496
                if (!agent->wildcard_subscription_active) {
5✔
1497
                        return sd_bus_reply_method_errorf(
×
1498
                                        m, SD_BUS_ERROR_FAILED, "No wildcard subscription active");
1499
                }
1500
                agent->wildcard_subscription_active = false;
5✔
1501
                return sd_bus_reply_method_return(m, "");
5✔
1502
        }
1503

1504
        _cleanup_free_ char *path = make_unit_path(unit);
53✔
1505
        if (path == NULL) {
24✔
1506
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to create the unit path");
×
1507
        }
1508

1509
        AgentUnitInfo *info = agent_get_unit_info(agent, path);
24✔
1510
        if (info == NULL || !info->subscribed) {
24✔
1511
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Not subscribed");
×
1512
        }
1513

1514
        info->subscribed = false;
24✔
1515
        agent_update_unit_infos_for(agent, info);
24✔
1516
        return sd_bus_reply_method_return(m, "");
24✔
1517
}
1518

1519
/*************************************************************************
1520
 ********** org.eclipse.bluechi.internal.Agent.StartDep ****
1521
 *************************************************************************/
1522

1523
static char *get_dep_unit(const char *unit_name) {
23✔
1524
        return strcat_dup("bluechi-dep@", unit_name);
23✔
1525
}
1526

1527
static int start_dep_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
12✔
1528
        UNUSED _cleanup_systemd_request_ SystemdRequest *req = userdata;
24✔
1529

1530
        if (sd_bus_message_is_method_error(m, NULL)) {
12✔
1531
                bc_log_errorf("Error starting dep service: %s", sd_bus_message_get_error(m)->message);
2✔
1532
        }
1533
        return 0;
24✔
1534
}
1535

1536
static int agent_method_start_dep(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
12✔
1537
        Agent *agent = userdata;
12✔
1538
        const char *unit = NULL;
12✔
1539

1540
        int r = sd_bus_message_read(m, "s", &unit);
12✔
1541
        if (r < 0) {
12✔
1542
                return sd_bus_reply_method_errorf(
×
1543
                                m, SD_BUS_ERROR_INVALID_ARGS, "Invalid argument for the unit");
1544
        }
1545

1546
        _cleanup_free_ char *dep_unit = get_dep_unit(unit);
24✔
1547
        if (dep_unit == NULL) {
12✔
1548
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to get the dependency unit");
×
1549
        }
1550

1551
        bc_log_infof("Starting dependency %s", dep_unit);
12✔
1552

1553
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(agent, m, "StartUnit");
24✔
1554
        if (req == NULL) {
12✔
1555
                return sd_bus_reply_method_errorf(
×
1556
                                m,
1557
                                SD_BUS_ERROR_FAILED,
1558
                                "Failed to create a systemd request for the StartUnit method");
1559
        }
1560

1561
        r = sd_bus_message_append(req->message, "ss", dep_unit, "replace");
12✔
1562
        if (r < 0) {
12✔
1563
                return sd_bus_reply_method_errorf(
×
1564
                                m,
1565
                                SD_BUS_ERROR_FAILED,
1566
                                "Failed to append the dependency unit and the replace: %s",
1567
                                strerror(-r));
1568
        }
1569

1570
        if (!systemd_request_start(req, start_dep_callback)) {
12✔
1571
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start a systemd request");
×
1572
        }
1573

1574
        return 0;
1575
}
1576

1577
/*************************************************************************
1578
 **** org.eclipse.bluechi.internal.Agent.StopDep ***********
1579
 *************************************************************************/
1580

1581
static int stop_dep_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
11✔
1582
        UNUSED _cleanup_systemd_request_ SystemdRequest *req = userdata;
22✔
1583

1584
        if (sd_bus_message_is_method_error(m, NULL)) {
11✔
1585
                bc_log_errorf("Error stopping dep service: %s", sd_bus_message_get_error(m)->message);
×
1586
        }
1587
        return 0;
22✔
1588
}
1589

1590
static int agent_method_stop_dep(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
11✔
1591
        Agent *agent = userdata;
11✔
1592
        const char *unit = NULL;
11✔
1593

1594
        int r = sd_bus_message_read(m, "s", &unit);
11✔
1595
        if (r < 0) {
11✔
1596
                return sd_bus_reply_method_errorf(
×
1597
                                m, SD_BUS_ERROR_INVALID_ARGS, "Invalid argument for the unit: %s", strerror(-r));
1598
        }
1599

1600
        _cleanup_free_ char *dep_unit = get_dep_unit(unit);
22✔
1601
        if (dep_unit == NULL) {
11✔
1602
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to get the dependency unit");
×
1603
        }
1604

1605
        bc_log_infof("Stopping dependency %s", dep_unit);
11✔
1606

1607
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(agent, m, "StopUnit");
22✔
1608
        if (req == NULL) {
11✔
1609
                return sd_bus_reply_method_errorf(
×
1610
                                m,
1611
                                SD_BUS_ERROR_FAILED,
1612
                                "Failed to create a systemd request for the StopUnit method");
1613
        }
1614

1615
        r = sd_bus_message_append(req->message, "ss", dep_unit, "replace");
11✔
1616
        if (r < 0) {
11✔
1617
                return sd_bus_reply_method_errorf(
×
1618
                                m,
1619
                                SD_BUS_ERROR_FAILED,
1620
                                "Failed to append the dependency unit and the replace: %s",
1621
                                strerror(-r));
1622
        }
1623

1624
        if (!systemd_request_start(req, stop_dep_callback)) {
11✔
1625
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start a systemd request");
×
1626
        }
1627

1628
        return 0;
1629
}
1630

1631

1632
/***************************************************************************
1633
 **** org.eclipse.bluechi.internal.Agent.EnableMetrics *******
1634
 ***************************************************************************/
1635

1636
static const sd_bus_vtable agent_metrics_vtable[] = {
1637
        SD_BUS_VTABLE_START(0),
1638
        SD_BUS_SIGNAL_WITH_NAMES(
1639
                        "AgentJobMetrics",
1640
                        "sst",
1641
                        SD_BUS_PARAM(unit) SD_BUS_PARAM(method) SD_BUS_PARAM(systemd_job_time_micros),
1642
                        0),
1643
        SD_BUS_VTABLE_END
1644
};
1645

1646
static int agent_method_enable_metrics(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
3✔
1647
        Agent *agent = userdata;
3✔
1648
        int r = 0;
3✔
1649

1650
        if (agent->metrics_enabled) {
3✔
1651
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Metrics already enabled");
×
1652
        }
1653
        agent->metrics_enabled = true;
3✔
1654
        r = sd_bus_add_object_vtable(
3✔
1655
                        agent->peer_dbus,
1656
                        &agent->metrics_slot,
1657
                        INTERNAL_AGENT_METRICS_OBJECT_PATH,
1658
                        INTERNAL_AGENT_METRICS_INTERFACE,
1659
                        agent_metrics_vtable,
1660
                        NULL);
1661
        if (r < 0) {
3✔
1662
                bc_log_errorf("Failed to add metrics vtable: %s", strerror(-r));
×
1663
                return r;
×
1664
        }
1665
        bc_log_debug("Metrics enabled");
3✔
1666
        return sd_bus_reply_method_return(m, "");
3✔
1667
}
1668

1669
/***************************************************************************
1670
 **** org.eclipse.bluechi.internal.Agent.DisableMetrics ******
1671
 ***************************************************************************/
1672

1673
static int agent_method_disable_metrics(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1674
        Agent *agent = userdata;
1✔
1675

1676
        if (!agent->metrics_enabled) {
1✔
1677
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Metrics already disabled");
×
1678
        }
1679
        agent->metrics_enabled = false;
1✔
1680
        sd_bus_slot_unrefp(&agent->metrics_slot);
1✔
1681
        agent->metrics_slot = NULL;
1✔
1682
        bc_log_debug("Metrics disabled");
1✔
1683
        return sd_bus_reply_method_return(m, "");
1✔
1684
}
1685

1686
/*************************************************************************
1687
 ************** org.eclipse.bluechi.Agent.SetNodeLogLevel  ***************
1688
 *************************************************************************/
1689

1690
static int agent_method_set_log_level(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) {
3✔
1691
        const char *level = NULL;
3✔
1692
        int r = sd_bus_message_read(m, "s", &level);
3✔
1693
        if (r < 0) {
3✔
1694
                bc_log_errorf("Failed to read Loglevel parameter: %s", strerror(-r));
×
1695
                return sd_bus_reply_method_errorf(
×
1696
                                m, SD_BUS_ERROR_FAILED, "Failed to read Loglevel parameter: %s", strerror(-r));
1697
        }
1698
        LogLevel loglevel = string_to_log_level(level);
3✔
1699
        if (loglevel == LOG_LEVEL_INVALID) {
3✔
1700
                bc_log_errorf("Invalid input for log level: %s", level);
1✔
1701
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Invalid input for log level");
1✔
1702
        }
1703
        bc_log_set_level(loglevel);
2✔
1704
        bc_log_infof("Log level changed to %s", level);
2✔
1705
        return sd_bus_reply_method_return(m, "");
2✔
1706
}
1707

1708

1709
/*******************************************************************
1710
 ************** org.eclipse.bluechi.Agent.JobCancel  ***************
1711
 *******************************************************************/
1712

1713
static int job_cancel_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1714
        _cleanup_systemd_request_ SystemdRequest *req = userdata;
1✔
1715

1716
        if (sd_bus_message_is_method_error(m, NULL)) {
1✔
1717
                return sd_bus_reply_method_error(req->request_message, sd_bus_message_get_error(m));
×
1718
        }
1719

1720
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
1✔
1721
        int r = sd_bus_message_new_method_return(req->request_message, &reply);
1✔
1722
        if (r < 0) {
1✔
1723
                return sd_bus_reply_method_errorf(
×
1724
                                req->request_message,
1725
                                SD_BUS_ERROR_FAILED,
1726
                                "Failed to create a reply message: %s",
1727
                                strerror(-r));
1728
        }
1729

1730
        return sd_bus_message_send(reply);
1✔
1731
}
1732

1733
static JobTracker *agent_find_jobtracker_by_bluechi_id(Agent *agent, uint32_t bc_job_id) {
1✔
1734
        JobTracker *track = NULL;
1✔
1735
        LIST_FOREACH(tracked_jobs, track, agent->tracked_jobs) {
1✔
1736
                AgentJobOp *op = (AgentJobOp *) track->userdata;
1✔
1737
                if (op->bc_job_id == bc_job_id) {
1✔
1738
                        return track;
1739
                }
1740
        }
1741
        return NULL;
1742
}
1743

1744
static int agent_method_job_cancel(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1745
        uint32_t bc_job_id = 0;
1✔
1746
        int r = sd_bus_message_read(m, "u", &bc_job_id);
1✔
1747
        if (r < 0) {
1✔
1748
                bc_log_errorf("Failed to read job ID: %s", strerror(-r));
×
1749
                return sd_bus_reply_method_errorf(
×
1750
                                m, SD_BUS_ERROR_FAILED, "Failed to read job ID: %s", strerror(-r));
1751
        }
1752

1753
        Agent *agent = (Agent *) userdata;
1✔
1754
        JobTracker *tracker = agent_find_jobtracker_by_bluechi_id(agent, bc_job_id);
1✔
1755
        if (tracker == NULL) {
1✔
1756
                return sd_bus_reply_method_errorf(
×
1757
                                m, SD_BUS_ERROR_FAILED, "No job with ID '%d' found", bc_job_id);
1758
        }
1759

1760
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request_full(
1✔
1761
                        agent, m, tracker->job_object_path, SYSTEMD_JOB_IFACE, "Cancel");
1✔
1762
        if (req == NULL) {
1✔
1763
                return sd_bus_reply_method_errorf(
×
1764
                                m,
1765
                                SD_BUS_ERROR_FAILED,
1766
                                "Failed to create a systemd request for the cancelling job '%d'",
1767
                                bc_job_id);
1768
        }
1769

1770
        if (!systemd_request_start(req, job_cancel_callback)) {
1✔
1771
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to start systemd request");
×
1772
        }
1773

1774
        return sd_bus_reply_method_return(m, "");
1✔
1775
}
1776

1777
static const sd_bus_vtable internal_agent_vtable[] = {
1778
        SD_BUS_VTABLE_START(0),
1779
        SD_BUS_METHOD("ListUnits", "", UNIT_INFO_STRUCT_ARRAY_TYPESTRING, agent_method_list_units, 0),
1780
        SD_BUS_METHOD("ListUnitFiles", "", UNIT_FILE_INFO_STRUCT_ARRAY_TYPESTRING, agent_method_list_unit_files, 0),
1781
        SD_BUS_METHOD("GetUnitFileState", "s", "s", agent_method_passthrough_to_systemd, 0),
1782
        SD_BUS_METHOD("GetUnitProperties", "ss", "a{sv}", agent_method_get_unit_properties, 0),
1783
        SD_BUS_METHOD("GetUnitProperty", "sss", "v", agent_method_get_unit_property, 0),
1784
        SD_BUS_METHOD("SetUnitProperties", "sba(sv)", "", agent_method_set_unit_properties, 0),
1785
        SD_BUS_METHOD("StartUnit", "ssu", "", agent_method_start_unit, 0),
1786
        SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", agent_method_passthrough_to_systemd, 0),
1787
        SD_BUS_METHOD("StopUnit", "ssu", "", agent_method_stop_unit, 0),
1788
        SD_BUS_METHOD("FreezeUnit", "s", "", agent_method_freeze_unit, 0),
1789
        SD_BUS_METHOD("ThawUnit", "s", "", agent_method_thaw_unit, 0),
1790
        SD_BUS_METHOD("RestartUnit", "ssu", "", agent_method_restart_unit, 0),
1791
        SD_BUS_METHOD("ReloadUnit", "ssu", "", agent_method_reload_unit, 0),
1792
        SD_BUS_METHOD("Subscribe", "s", "", agent_method_subscribe, 0),
1793
        SD_BUS_METHOD("Unsubscribe", "s", "", agent_method_unsubscribe, 0),
1794
        SD_BUS_METHOD("EnableMetrics", "", "", agent_method_enable_metrics, 0),
1795
        SD_BUS_METHOD("DisableMetrics", "", "", agent_method_disable_metrics, 0),
1796
        SD_BUS_METHOD("SetLogLevel", "s", "", agent_method_set_log_level, 0),
1797
        SD_BUS_METHOD("JobCancel", "u", "", agent_method_job_cancel, 0),
1798
        SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", agent_method_passthrough_to_systemd, 0),
1799
        SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", agent_method_passthrough_to_systemd, 0),
1800
        SD_BUS_METHOD("Reload", "", "", agent_method_passthrough_to_systemd, 0),
1801
        SD_BUS_METHOD("ResetFailed", "", "", agent_method_passthrough_to_systemd, 0),
1802
        SD_BUS_METHOD("ResetFailedUnit", "s", "", agent_method_passthrough_to_systemd, 0),
1803
        SD_BUS_METHOD("KillUnit", "ssi", "", agent_method_passthrough_to_systemd, 0),
1804
        SD_BUS_METHOD("StartDep", "s", "", agent_method_start_dep, 0),
1805
        SD_BUS_METHOD("StopDep", "s", "", agent_method_stop_dep, 0),
1806
        SD_BUS_METHOD("GetDefaultTarget", "", "s", agent_method_passthrough_to_systemd, 0),
1807
        SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", agent_method_passthrough_to_systemd, 0),
1808
        SD_BUS_SIGNAL_WITH_NAMES("JobDone", "us", SD_BUS_PARAM(id) SD_BUS_PARAM(result), 0),
1809
        SD_BUS_SIGNAL_WITH_NAMES("JobStateChanged", "us", SD_BUS_PARAM(id) SD_BUS_PARAM(state), 0),
1810
        SD_BUS_SIGNAL_WITH_NAMES(
1811
                        "UnitPropertiesChanged",
1812
                        "ssa{sv}",
1813
                        SD_BUS_PARAM(unit) SD_BUS_PARAM(iface) SD_BUS_PARAM(properties),
1814
                        0),
1815
        SD_BUS_SIGNAL_WITH_NAMES("UnitNew", "ss", SD_BUS_PARAM(unit) SD_BUS_PARAM(reason), 0),
1816
        SD_BUS_SIGNAL_WITH_NAMES(
1817
                        "UnitStateChanged",
1818
                        "ssss",
1819
                        SD_BUS_PARAM(unit) SD_BUS_PARAM(active_state) SD_BUS_PARAM(substate)
1820
                                        SD_BUS_PARAM(reason),
1821
                        0),
1822
        SD_BUS_SIGNAL_WITH_NAMES("UnitRemoved", "s", SD_BUS_PARAM(unit), 0),
1823
        SD_BUS_SIGNAL("Heartbeat", "", 0),
1824
        SD_BUS_VTABLE_END
1825
};
1826

1827
/*************************************************************************
1828
 ********** org.eclipse.bluechi.Agent.CreateProxy **********
1829
 *************************************************************************/
1830

1831
static ProxyService *agent_find_proxy(
21✔
1832
                Agent *agent, const char *local_service_name, const char *node_name, const char *unit_name) {
1833
        ProxyService *proxy = NULL;
21✔
1834

1835
        LIST_FOREACH(proxy_services, proxy, agent->proxy_services) {
21✔
1836
                if (streq(proxy->local_service_name, local_service_name) &&
5✔
1837
                    streq(proxy->node_name, node_name) && streq(proxy->unit_name, unit_name)) {
5✔
1838
                        return proxy;
1839
                }
1840
        }
1841

1842
        return NULL;
1843
}
1844

1845
static int agent_method_create_proxy(UNUSED sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
13✔
1846
        Agent *agent = (Agent *) userdata;
13✔
1847
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
26✔
1848
        const char *local_service_name = NULL;
13✔
1849
        const char *node_name = NULL;
13✔
1850
        const char *unit_name = NULL;
13✔
1851
        int r = sd_bus_message_read(m, "sss", &local_service_name, &node_name, &unit_name);
13✔
1852
        if (r < 0) {
13✔
1853
                return sd_bus_reply_method_errorf(
×
1854
                                m,
1855
                                SD_BUS_ERROR_INVALID_ARGS,
1856
                                "Invalid argument for: local service name, node name, or unit name: %s",
1857
                                strerror(-r));
1858
        }
1859

1860
        bc_log_infof("CreateProxy request from %s", local_service_name);
13✔
1861

1862
        ProxyService *old_proxy = agent_find_proxy(agent, local_service_name, node_name, unit_name);
13✔
1863
        if (old_proxy) {
13✔
1864
                return sd_bus_reply_method_errorf(reply, SD_BUS_ERROR_ADDRESS_IN_USE, "Proxy already exists");
×
1865
        }
1866

1867
        _cleanup_proxy_service_ ProxyService *proxy = proxy_service_new(
26✔
1868
                        agent, local_service_name, node_name, unit_name, m);
1869
        if (proxy == NULL) {
13✔
1870
                return sd_bus_reply_method_errorf(
×
1871
                                reply, SD_BUS_ERROR_FAILED, "Failed to create a proxy service");
1872
        }
1873

1874
        if (!proxy_service_export(proxy)) {
13✔
1875
                return sd_bus_reply_method_errorf(
×
1876
                                reply, SD_BUS_ERROR_FAILED, "Failed to export a proxy service");
1877
        }
1878

1879
        r = proxy_service_emit_proxy_new(proxy);
13✔
1880
        if (r < 0 && r != -ENOTCONN) {
13✔
1881
                bc_log_errorf("Failed to emit ProxyNew signal: %s", strerror(-r));
×
1882
                return sd_bus_reply_method_errorf(
×
1883
                                m, SD_BUS_ERROR_FAILED, "Failed to emit a proxy service: %s", strerror(-r));
1884
        }
1885

1886
        LIST_APPEND(proxy_services, agent->proxy_services, proxy_service_ref(proxy));
13✔
1887

1888
        return 1;
1889
}
1890

1891
/*************************************************************************
1892
 **** org.eclipse.bluechi.Agent.RemoveProxy ****************
1893
 *************************************************************************/
1894

1895
/* This is called when the proxy service is shut down, via the
1896
 * `ExecStop=bluechi-proxy remove` command being called. This will
1897
 * happen when no services depends on the proxy anymore, as well as
1898
 * when the agent explicitly tells the proxy to stop.
1899
 */
1900

1901
static int agent_method_remove_proxy(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) {
8✔
1902
        Agent *agent = (Agent *) userdata;
8✔
1903
        const char *local_service_name = NULL;
8✔
1904
        const char *node_name = NULL;
8✔
1905
        const char *unit_name = NULL;
8✔
1906
        int r = sd_bus_message_read(m, "sss", &local_service_name, &node_name, &unit_name);
8✔
1907
        if (r < 0) {
8✔
1908
                return sd_bus_reply_method_errorf(
×
1909
                                m,
1910
                                SD_BUS_ERROR_INVALID_ARGS,
1911
                                "Invalid argument for: local service name, node name, or unit name: %s",
1912
                                strerror(-r));
1913
        }
1914

1915
        bc_log_infof("RemoveProxy request from %s", local_service_name);
8✔
1916

1917
        ProxyService *proxy = agent_find_proxy(agent, local_service_name, node_name, unit_name);
8✔
1918
        if (proxy == NULL) {
8✔
1919
                /* NOTE: This happens pretty often, when we stopped the proxy service ourselves
1920
                   and removed the proxy, but then the ExecStop triggers, so we sent a success here. */
1921
                return sd_bus_reply_method_return(m, "");
3✔
1922
        }
1923

1924
        /* We got this because the proxy is shutting down, no need to manually do it */
1925
        proxy->dont_stop_proxy = true;
5✔
1926

1927
        agent_remove_proxy(agent, proxy, true);
5✔
1928

1929
        return sd_bus_reply_method_return(m, "");
5✔
1930
}
1931

1932

1933
/*************************************************************************
1934
 **** org.eclipse.bluechi.Agent.SwitchController ******
1935
 *************************************************************************/
1936

1937
static int agent_method_switch_controller(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
1✔
1938
        Agent *agent = userdata;
1✔
1939
        const char *dbus_address = NULL;
1✔
1940

1941
        int r = sd_bus_message_read(m, "s", &dbus_address);
1✔
1942
        if (r < 0) {
1✔
1943
                bc_log_errorf("Failed to read D-Bus address parameter: %s", strerror(-r));
×
1944
                return sd_bus_reply_method_errorf(
×
1945
                                m,
1946
                                SD_BUS_ERROR_FAILED,
1947
                                "Failed to read D-Bus address parameter: %s",
1948
                                strerror(-r));
1949
        }
1950

1951
        if (agent->assembled_controller_address && streq(agent->assembled_controller_address, dbus_address)) {
1✔
1952
                return sd_bus_reply_method_errorf(
×
1953
                                m,
1954
                                SD_BUS_ERROR_FAILED,
1955
                                "Failed to switch controller because already connected to the controller");
1956
        }
1957

1958
        /* The assembled controller address is the field used for the ControllerAddress property.
1959
         * However, this field gets assembled in the reconnect based on the configuration of host + port
1960
         * or controller address.
1961
         * So in order to enable emitting the address changed signal be sent before the disconnect AND keep
1962
         * the new value, both fields need to be set with the new address.
1963
         */
1964
        if (!agent_set_assembled_controller_address(agent, dbus_address) ||
2✔
1965
            !agent_set_controller_address(agent, dbus_address)) {
1✔
1966
                bc_log_error("Failed to set controller address");
×
1967
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to set controller address");
×
1968
        }
1969
        r = sd_bus_emit_properties_changed(
1✔
1970
                        agent->api_bus, BC_AGENT_OBJECT_PATH, AGENT_INTERFACE, "ControllerAddress", NULL);
1971
        if (r < 0) {
1✔
1972
                bc_log_errorf("Failed to emit controller address property changed: %s", strerror(-r));
×
1973
        }
1974

1975
        agent_disconnected(NULL, userdata, NULL);
1✔
1976

1977
        return sd_bus_reply_method_return(m, "");
1✔
1978
}
1979

1980

1981
/*************************************************************************
1982
 **** org.eclipse.bluechi.Agent.Status ****************
1983
 *************************************************************************/
1984

1985
static int agent_property_get_status(
324✔
1986
                UNUSED sd_bus *bus,
1987
                UNUSED const char *path,
1988
                UNUSED const char *interface,
1989
                UNUSED const char *property,
1990
                sd_bus_message *reply,
1991
                void *userdata,
1992
                UNUSED sd_bus_error *ret_error) {
1993
        Agent *agent = userdata;
324✔
1994

1995
        return sd_bus_message_append(reply, "s", agent_is_online(agent));
324✔
1996
}
1997

1998
/*************************************************************************
1999
 **** org.eclipse.bluechi.Agent.LogLevel ****************
2000
 *************************************************************************/
2001

2002
static int agent_property_get_log_level(
2✔
2003
                UNUSED sd_bus *bus,
2004
                UNUSED const char *path,
2005
                UNUSED const char *interface,
2006
                UNUSED const char *property,
2007
                sd_bus_message *reply,
2008
                UNUSED void *userdata,
2009
                UNUSED sd_bus_error *ret_error) {
2010
        const char *log_level = log_level_to_string(bc_log_get_level());
2✔
2011
        return sd_bus_message_append(reply, "s", log_level);
2✔
2012
}
2013

2014
/*************************************************************************
2015
 **** org.eclipse.bluechi.Agent.LogTarget ****************
2016
 *************************************************************************/
2017

2018
static int agent_property_get_log_target(
1✔
2019
                UNUSED sd_bus *bus,
2020
                UNUSED const char *path,
2021
                UNUSED const char *interface,
2022
                UNUSED const char *property,
2023
                sd_bus_message *reply,
2024
                UNUSED void *userdata,
2025
                UNUSED sd_bus_error *ret_error) {
2026
        return sd_bus_message_append(reply, "s", log_target_to_str(bc_log_get_log_fn()));
1✔
2027
}
2028

2029

2030
static const sd_bus_vtable agent_vtable[] = {
2031
        SD_BUS_VTABLE_START(0),
2032
        SD_BUS_METHOD("CreateProxy", "sss", "", agent_method_create_proxy, 0),
2033
        SD_BUS_METHOD("RemoveProxy", "sss", "", agent_method_remove_proxy, 0),
2034
        SD_BUS_METHOD("JobCancel", "u", "", agent_method_job_cancel, 0),
2035
        SD_BUS_METHOD("SwitchController", "s", "", agent_method_switch_controller, 0),
2036

2037
        SD_BUS_PROPERTY("Status", "s", agent_property_get_status, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
2038
        SD_BUS_PROPERTY("LogLevel", "s", agent_property_get_log_level, 0, SD_BUS_VTABLE_PROPERTY_EXPLICIT),
2039
        SD_BUS_PROPERTY("LogTarget", "s", agent_property_get_log_target, 0, SD_BUS_VTABLE_PROPERTY_CONST),
2040
        SD_BUS_PROPERTY("DisconnectTimestamp",
2041
                        "t",
2042
                        NULL,
2043
                        offsetof(Agent, disconnect_timestamp),
2044
                        SD_BUS_VTABLE_PROPERTY_EXPLICIT),
2045
        SD_BUS_PROPERTY("DisconnectTimestampMonotonic",
2046
                        "t",
2047
                        NULL,
2048
                        offsetof(Agent, disconnect_timestamp_monotonic),
2049
                        SD_BUS_VTABLE_PROPERTY_EXPLICIT),
2050
        SD_BUS_PROPERTY("LastSeenTimestamp",
2051
                        "t",
2052
                        NULL,
2053
                        offsetof(Agent, controller_last_seen),
2054
                        SD_BUS_VTABLE_PROPERTY_EXPLICIT),
2055
        SD_BUS_PROPERTY("LastSeenTimestampMonotonic",
2056
                        "t",
2057
                        NULL,
2058
                        offsetof(Agent, controller_last_seen_monotonic),
2059
                        SD_BUS_VTABLE_PROPERTY_EXPLICIT),
2060
        SD_BUS_PROPERTY("ControllerAddress",
2061
                        "s",
2062
                        NULL,
2063
                        offsetof(Agent, assembled_controller_address),
2064
                        SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
2065
        SD_BUS_VTABLE_END
2066
};
2067

2068

2069
static void job_tracker_free(JobTracker *track) {
63✔
2070
        if (track->userdata && track->free_userdata) {
63✔
2071
                track->free_userdata(track->userdata);
63✔
2072
        }
2073
        free_and_null(track->job_object_path);
63✔
2074
        free(track);
63✔
2075
}
63✔
2076

2077
DEFINE_CLEANUP_FUNC(JobTracker, job_tracker_free)
×
2078
#define _cleanup_job_tracker_ _cleanup_(job_tracker_freep)
2079

2080
static bool
2081
                agent_track_job(Agent *agent,
66✔
2082
                                const char *job_object_path,
2083
                                job_tracker_callback callback,
2084
                                void *userdata,
2085
                                free_func_t free_userdata) {
2086
        _cleanup_job_tracker_ JobTracker *track = malloc0(sizeof(JobTracker));
132✔
2087
        if (track == NULL) {
66✔
2088
                return false;
2089
        }
2090

2091
        track->job_object_path = strdup(job_object_path);
66✔
2092
        if (track->job_object_path == NULL) {
66✔
2093
                return false;
2094
        }
2095

2096
// Disabling -Wanalyzer-malloc-leak temporarily due to false-positive
2097
//      Leak detected is based on steal_pointer setting track=NULL and, subsequently,
2098
//      not freeing allocated job_object_path. This is desired since the actual instance
2099
//      is added to and managed by the tracked_jobs list of the agent.
2100
#pragma GCC diagnostic push
2101
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
2102

2103
        track->callback = callback;
66✔
2104
        track->userdata = userdata;
66✔
2105
        track->free_userdata = free_userdata;
66✔
2106
        LIST_INIT(tracked_jobs, track);
66✔
2107

2108
        LIST_PREPEND(tracked_jobs, agent->tracked_jobs, steal_pointer(&track));
66✔
2109

2110
        return true;
66✔
2111
}
2112
#pragma GCC diagnostic pop
2113

2114

2115
static int agent_match_job_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
37✔
2116
        Agent *agent = userdata;
37✔
2117
        const char *interface = NULL;
37✔
2118
        const char *state = NULL;
37✔
2119
        const char *object_path = sd_bus_message_get_path(m);
37✔
2120
        JobTracker *track = NULL;
37✔
2121
        AgentJobOp *op = NULL;
37✔
2122

2123
        int r = sd_bus_message_read(m, "s", &interface);
37✔
2124
        if (r < 0) {
37✔
2125
                bc_log_errorf("Failed to read job property: %s", strerror(-r));
×
2126
                return r;
×
2127
        }
2128

2129
        /* Only handle Job iface changes */
2130
        if (!streq(interface, SYSTEMD_JOB_IFACE)) {
37✔
2131
                return 0;
2132
        }
2133

2134
        /* Look for tracked jobs */
2135
        LIST_FOREACH(tracked_jobs, track, agent->tracked_jobs) {
50✔
2136
                if (streq(track->job_object_path, object_path)) {
19✔
2137
                        op = track->userdata;
6✔
2138
                        break;
6✔
2139
                }
2140
        }
2141

2142
        if (op == NULL) {
37✔
2143
                return 0;
2144
        }
2145

2146
        r = bus_parse_property_string(m, "State", &state);
6✔
2147
        if (r < 0) {
6✔
2148
                if (r == -ENOENT) {
×
2149
                        return 0; /* Some other property changed */
2150
                }
2151
                bc_log_errorf("Failed to get job property: %s", strerror(-r));
×
2152
                return r;
×
2153
        }
2154

2155
        r = sd_bus_emit_signal(
6✔
2156
                        agent->peer_dbus,
2157
                        INTERNAL_AGENT_OBJECT_PATH,
2158
                        INTERNAL_AGENT_INTERFACE,
2159
                        "JobStateChanged",
2160
                        "us",
2161
                        op->bc_job_id,
2162
                        state);
2163
        if (r < 0) {
6✔
2164
                bc_log_errorf("Failed to emit JobStateChanged: %s", strerror(-r));
×
2165
        }
2166

2167
        return 0;
2168
}
2169

2170
static int agent_match_unit_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
2,301✔
2171
        Agent *agent = userdata;
2,301✔
2172
        const char *interface = NULL;
2,301✔
2173

2174
        int r = sd_bus_message_read(m, "s", &interface);
2,301✔
2175
        if (r < 0) {
2,301✔
2176
                return r;
2,301✔
2177
        }
2178

2179
        AgentUnitInfo *info = agent_get_unit_info(agent, sd_bus_message_get_path(m));
2,301✔
2180
        if (info == NULL) {
2,301✔
2181
                return 0;
2182
        }
2183

2184
        if (streq(interface, "org.freedesktop.systemd1.Unit")) {
2,301✔
2185
                bool state_changed = unit_info_update_state(info, m);
1,484✔
2186
                if (state_changed && (info->subscribed || agent->wildcard_subscription_active)) {
1,484✔
2187
                        agent_emit_unit_state_changed(agent, info, "real");
49✔
2188
                }
2189
        }
2190

2191
        if (!info->subscribed && !agent->wildcard_subscription_active) {
2,301✔
2192
                return 0;
2193
        }
2194

2195
        /* Rewind and skip to copy content again */
2196
        (void) sd_bus_message_rewind(m, true);
220✔
2197
        (void) sd_bus_message_skip(m, "s"); // re-skip interface
220✔
2198

2199
        bc_log_debugf("Sending UnitPropertiesChanged %s", info->unit);
220✔
2200

2201
        /* Forward the property changes */
2202
        _cleanup_sd_bus_message_ sd_bus_message *sig = NULL;
2,301✔
2203
        r = sd_bus_message_new_signal(
220✔
2204
                        agent->peer_dbus,
2205
                        &sig,
2206
                        INTERNAL_AGENT_OBJECT_PATH,
2207
                        INTERNAL_AGENT_INTERFACE,
2208
                        "UnitPropertiesChanged");
2209
        if (r < 0) {
220✔
2210
                return r;
2211
        }
2212

2213
        r = sd_bus_message_append(sig, "s", info->unit);
220✔
2214
        if (r < 0) {
220✔
2215
                return r;
2216
        }
2217

2218
        r = sd_bus_message_append(sig, "s", interface);
220✔
2219
        if (r < 0) {
220✔
2220
                return r;
2221
        }
2222

2223
        r = sd_bus_message_copy(sig, m, false);
220✔
2224
        if (r < 0) {
220✔
2225
                return r;
2226
        }
2227

2228
        return sd_bus_send(agent->peer_dbus, sig, NULL);
220✔
2229
}
2230

2231
static int agent_match_unit_new(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
9,230✔
2232
        Agent *agent = userdata;
9,230✔
2233
        const char *unit_name = NULL;
9,230✔
2234
        const char *path = NULL;
9,230✔
2235

2236
        int r = sd_bus_message_read(m, "so", &unit_name, &path);
9,230✔
2237
        if (r < 0) {
9,230✔
2238
                return r;
9,230✔
2239
        }
2240

2241
        AgentUnitInfo *info = agent_ensure_unit_info(agent, unit_name);
9,230✔
2242
        if (info == NULL) {
9,230✔
2243
                return 0;
2244
        }
2245

2246
        info->loaded = true;
9,230✔
2247

2248
// Disabling -Wanalyzer-malloc-leak temporarily due to false-positive
2249
//      Leak detection does not take into account that the AgentUnitInfo info instance was
2250
//      added to and is managed by the hashmap
2251
#pragma GCC diagnostic push
2252
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
2253

2254
        /* Systemd services start in inactive(dead) state, so use this as the first
2255
         * state. This way we can detect when the state changed without sporadic
2256
         * changes to "dead".
2257
         */
2258
        info->active_state = UNIT_INACTIVE;
9,230✔
2259
        if (info->substate != NULL) {
9,230✔
2260
                free_and_null(info->substate);
×
2261
        }
2262
        info->substate = strdup("dead");
9,230✔
2263

2264
        if (info->subscribed || agent->wildcard_subscription_active) {
9,230✔
2265
                /* Forward the event */
2266
                agent_emit_unit_new(agent, info, "real");
28✔
2267
        }
2268

2269

2270
        return 0;
2271
}
2272
#pragma GCC diagnostic pop
2273

2274
static int agent_match_unit_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
9,178✔
2275
        Agent *agent = userdata;
9,178✔
2276
        const char *id = NULL;
9,178✔
2277
        const char *path = NULL;
9,178✔
2278

2279
        int r = sd_bus_message_read(m, "so", &id, &path);
9,178✔
2280
        if (r < 0) {
9,178✔
2281
                return r;
9,178✔
2282
        }
2283

2284
        AgentUnitInfo *info = agent_get_unit_info(agent, path);
9,178✔
2285
        if (info == NULL) {
9,178✔
2286
                return 0;
2287
        }
2288

2289
        info->loaded = false;
9,178✔
2290
        info->active_state = _UNIT_ACTIVE_STATE_INVALID;
9,178✔
2291
        free_and_null(info->substate);
9,178✔
2292

2293
        if (info->subscribed || agent->wildcard_subscription_active) {
9,178✔
2294
                /* Forward the event */
2295
                agent_emit_unit_removed(agent, info);
19✔
2296
        }
2297

2298
        /* Maybe remove if unloaded and no other interest in it */
2299
        agent_update_unit_infos_for(agent, info);
9,178✔
2300

2301
        return 1;
2302
}
2303

2304

2305
static int agent_match_job_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
147✔
2306
        Agent *agent = userdata;
147✔
2307
        const char *job_path = NULL;
147✔
2308
        const char *unit = NULL;
147✔
2309
        const char *result = NULL;
147✔
2310
        JobTracker *track = NULL, *next_track = NULL;
147✔
2311
        uint32_t id = 0;
147✔
2312
        int r = 0;
147✔
2313

2314
        r = sd_bus_message_read(m, "uoss", &id, &job_path, &unit, &result);
147✔
2315
        if (r < 0) {
147✔
2316
                bc_log_errorf("Can't parse job result: %s", strerror(-r));
×
2317
                return r;
×
2318
        }
2319

2320
        (void) sd_bus_message_rewind(m, true);
147✔
2321

2322
        LIST_FOREACH_SAFE(tracked_jobs, track, next_track, agent->tracked_jobs) {
173✔
2323
                if (streq(track->job_object_path, job_path)) {
89✔
2324
                        LIST_REMOVE(tracked_jobs, agent->tracked_jobs, track);
63✔
2325
                        track->callback(m, result, track->userdata);
63✔
2326
                        job_tracker_free(track);
63✔
2327
                        break;
2328
                }
2329
        }
2330

2331
        return 0;
2332
}
2333

2334
static int agent_match_heartbeat(UNUSED sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
2✔
2335
        Agent *agent = userdata;
2✔
2336
        uint64_t now = 0;
2✔
2337
        uint64_t now_monotonic = 0;
2✔
2338

2339
        now = get_time_micros();
2✔
2340
        if (now == USEC_INFINITY) {
2✔
2341
                bc_log_error("Failed to get current time on heartbeat");
×
2342
                return 0;
×
2343
        }
2344

2345
        now_monotonic = get_time_micros_monotonic();
2✔
2346
        if (now_monotonic == USEC_INFINITY) {
2✔
2347
                bc_log_error("Failed to get current monotonic time on heartbeat");
×
2348
                return 0;
×
2349
        }
2350

2351
        agent->controller_last_seen = now;
2✔
2352
        agent->controller_last_seen_monotonic = now_monotonic;
2✔
2353
        return 1;
2✔
2354
}
2355

2356
static int debug_systemd_message_handler(
2357
                sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) {
2358
        bc_log_infof("Incoming message from systemd: path: %s, iface: %s, member: %s, signature: '%s'",
2359
                     sd_bus_message_get_path(m),
2360
                     sd_bus_message_get_interface(m),
2361
                     sd_bus_message_get_member(m),
2362
                     sd_bus_message_get_signature(m, true));
2363
        if (DEBUG_SYSTEMD_MESSAGES_CONTENT) {
2364
                sd_bus_message_dump(m, stderr, 0);
2365
                sd_bus_message_rewind(m, true);
2366
        }
2367
        return 0;
2368
}
2369

2370
int agent_init_units(Agent *agent, sd_bus_message *m) {
150✔
2371
        int r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, UNIT_INFO_STRUCT_TYPESTRING);
150✔
2372
        if (r < 0) {
150✔
2373
                return r;
2374
        }
2375

2376
// Disabling -Wanalyzer-malloc-leak temporarily due to false-positive
2377
//      Leak detection does not take into account that the AgentUnitInfo info instance was
2378
//      added to and is managed by the hashmap
2379
#pragma GCC diagnostic push
2380
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
2381
        while (sd_bus_message_at_end(m, false) == 0) {
17,260✔
2382
                r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, UNIT_INFO_TYPESTRING);
17,110✔
2383
                if (r < 0) {
17,110✔
2384
                        return r;
×
2385
                }
2386

2387
                // NOLINTBEGIN(cppcoreguidelines-init-variables)
2388
                const char *name, *desc, *load_state, *active_state, *sub_state, *follower, *object_path,
17,110✔
2389
                                *job_type, *job_object_path;
2390
                uint32_t job_id;
17,110✔
2391
                // NOLINTEND(cppcoreguidelines-init-variables)
2392

2393
                r = sd_bus_message_read(
17,110✔
2394
                                m,
2395
                                UNIT_INFO_TYPESTRING,
2396
                                &name,
2397
                                &desc,
2398
                                &load_state,
2399
                                &active_state,
2400
                                &sub_state,
2401
                                &follower,
2402
                                &object_path,
2403
                                &job_id,
2404
                                &job_type,
2405
                                &job_object_path);
2406
                if (r < 0) {
17,110✔
2407
                        return r;
2408
                }
2409

2410
                AgentUnitInfo *info = agent_ensure_unit_info(agent, name);
17,110✔
2411
                if (info) {
17,110✔
2412
                        assert(streq(info->object_path, object_path));
17,110✔
2413
                        info->loaded = true;
17,110✔
2414
                        info->active_state = active_state_from_string(active_state);
17,110✔
2415
                        if (info->substate != NULL) {
17,110✔
2416
                                free_and_null(info->substate);
×
2417
                        }
2418
                        info->substate = strdup(sub_state);
17,110✔
2419
                }
2420

2421
                r = sd_bus_message_exit_container(m);
17,110✔
2422
                if (r < 0) {
17,110✔
2423
                        return r;
2424
                }
2425
        }
2426
#pragma GCC diagnostic pop
2427

2428
        r = sd_bus_message_exit_container(m);
150✔
2429
        if (r < 0) {
150✔
2430
                return r;
×
2431
        }
2432

2433
        return 0;
2434
}
2435

2436
static bool ensure_assembled_controller_address(Agent *agent) {
174✔
2437
        int r = 0;
174✔
2438

2439
        if (agent->assembled_controller_address != NULL) {
174✔
2440
                return true;
174✔
2441
        }
2442

2443
        if (agent->controller_address != NULL) {
173✔
2444
                return agent_set_assembled_controller_address(agent, agent->controller_address);
7✔
2445
        }
2446

2447
        if (agent->host == NULL) {
166✔
2448
                bc_log_errorf("No controller host specified for agent '%s'", agent->name);
×
2449
                return false;
×
2450
        }
2451

2452
        char *ip_address = agent->host;
166✔
2453
        bool host_is_ipv4 = is_ipv4(ip_address);
166✔
2454
        bool host_is_ipv6 = is_ipv6(ip_address);
166✔
2455

2456
        _cleanup_free_ char *resolved_ip_address = NULL;
174✔
2457
        if (!host_is_ipv4 && !host_is_ipv6) {
166✔
2458
                int r = get_address(ip_address, &resolved_ip_address, getaddrinfo);
4✔
2459
                if (r < 0) {
4✔
2460
                        bc_log_errorf("Failed to get IP address from host '%s': %s", agent->host, strerror(-r));
×
2461
                        return false;
2462
                }
2463
                bc_log_infof("Translated '%s' to '%s'", ip_address, resolved_ip_address);
4✔
2464
                ip_address = resolved_ip_address;
4✔
2465
        }
2466

2467
        if (host_is_ipv4 || is_ipv4(ip_address)) {
166✔
2468
                struct sockaddr_in host;
162✔
2469
                memset(&host, 0, sizeof(host));
162✔
2470
                host.sin_family = AF_INET;
162✔
2471
                host.sin_port = htons(agent->port);
162✔
2472
                r = inet_pton(AF_INET, ip_address, &host.sin_addr);
162✔
2473
                if (r < 1) {
162✔
2474
                        bc_log_errorf("INET4: Invalid host option '%s'", ip_address);
×
2475
                        return false;
×
2476
                }
2477
                _cleanup_free_ char *assembled_controller_address = assemble_tcp_address(&host);
324✔
2478
                if (assembled_controller_address == NULL) {
162✔
2479
                        return false;
×
2480
                }
2481
                agent_set_assembled_controller_address(agent, assembled_controller_address);
162✔
2482
        } else if (host_is_ipv6 || is_ipv6(ip_address)) {
4✔
2483
                struct sockaddr_in6 host6;
4✔
2484
                memset(&host6, 0, sizeof(host6));
4✔
2485
                host6.sin6_family = AF_INET6;
4✔
2486
                host6.sin6_port = htons(agent->port);
4✔
2487
                r = inet_pton(AF_INET6, ip_address, &host6.sin6_addr);
4✔
2488
                if (r < 1) {
4✔
2489
                        bc_log_errorf("INET6: Invalid host option '%s'", ip_address);
×
2490
                        return false;
×
2491
                }
2492
                _cleanup_free_ char *assembled_controller_address = assemble_tcp_address_v6(&host6);
8✔
2493
                if (assembled_controller_address == NULL) {
4✔
2494
                        return false;
×
2495
                }
2496
                agent_set_assembled_controller_address(agent, assembled_controller_address);
4✔
2497
        } else {
2498
                bc_log_errorf("Unknown protocol for '%s'", ip_address);
×
2499
        }
2500

2501
        return agent->assembled_controller_address != NULL;
166✔
2502
}
2503

2504
bool agent_start(Agent *agent) {
150✔
2505
        int r = 0;
150✔
2506
        sd_bus_error error = SD_BUS_ERROR_NULL;
150✔
2507

2508
        bc_log_infof("Starting bluechi-agent %s", CONFIG_H_BC_VERSION);
150✔
2509

2510
        if (agent == NULL) {
150✔
2511
                return false;
150✔
2512
        }
2513

2514
        if (agent->name == NULL) {
150✔
2515
                bc_log_error("No agent name specified");
×
2516
                return false;
×
2517
        }
2518

2519
        if (!ensure_assembled_controller_address(agent)) {
150✔
2520
                return false;
2521
        }
2522

2523
        /* If systemd --user, we need to be on the user bus for the proxy to work */
2524
        if (agent->systemd_user || ALWAYS_USER_API_BUS) {
150✔
2525
                agent->api_bus = user_bus_open(agent->event);
×
2526
        } else {
2527
                agent->api_bus = system_bus_open(agent->event);
150✔
2528
        }
2529

2530
        if (agent->api_bus == NULL) {
150✔
2531
                bc_log_error("Failed to open api dbus");
×
2532
                return false;
×
2533
        }
2534

2535
        r = sd_bus_add_object_vtable(
150✔
2536
                        agent->api_bus, NULL, BC_AGENT_OBJECT_PATH, AGENT_INTERFACE, agent_vtable, agent);
2537
        if (r < 0) {
150✔
2538
                bc_log_errorf("Failed to add agent vtable: %s", strerror(-r));
×
2539
                return false;
×
2540
        }
2541

2542
        r = sd_bus_request_name(agent->api_bus, agent->api_bus_service_name, SD_BUS_NAME_REPLACE_EXISTING);
150✔
2543
        if (r < 0) {
150✔
2544
                bc_log_errorf("Failed to acquire service name on api dbus: %s", strerror(-r));
×
2545
                return false;
×
2546
        }
2547

2548
        if (agent->systemd_user) {
150✔
2549
                agent->systemd_dbus = user_bus_open(agent->event);
×
2550
        } else {
2551
                agent->systemd_dbus = systemd_bus_open(agent->event);
150✔
2552
        }
2553
        if (agent->systemd_dbus == NULL) {
150✔
2554
                bc_log_error("Failed to open systemd dbus");
×
2555
                return false;
×
2556
        }
2557

2558
        _cleanup_sd_bus_message_ sd_bus_message *sub_m = NULL;
150✔
2559
        r = sd_bus_call_method(
150✔
2560
                        agent->systemd_dbus,
2561
                        SYSTEMD_BUS_NAME,
2562
                        SYSTEMD_OBJECT_PATH,
2563
                        SYSTEMD_MANAGER_IFACE,
2564
                        "Subscribe",
2565
                        &error,
2566
                        &sub_m,
2567
                        "");
2568
        if (r < 0) {
150✔
2569
                bc_log_errorf("Failed to issue subscribe call: %s", error.message);
×
2570
                sd_bus_error_free(&error);
×
2571
                return false;
2572
        }
2573

2574
        r = sd_bus_add_match(
150✔
2575
                        agent->systemd_dbus,
2576
                        NULL,
2577
                        "type='signal',sender='org.freedesktop.systemd1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/freedesktop/systemd1/job'",
2578
                        agent_match_job_changed,
2579
                        agent);
2580
        if (r < 0) {
150✔
2581
                bc_log_errorf("Failed to add match: %s", strerror(-r));
×
2582
                return false;
2583
        }
2584

2585
        r = sd_bus_add_match(
150✔
2586
                        agent->systemd_dbus,
2587
                        NULL,
2588
                        "type='signal',sender='org.freedesktop.systemd1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/freedesktop/systemd1/unit'",
2589
                        agent_match_unit_changed,
2590
                        agent);
2591
        if (r < 0) {
150✔
2592
                bc_log_errorf("Failed to add match: %s", strerror(-r));
×
2593
                return false;
2594
        }
2595

2596
        r = sd_bus_match_signal(
150✔
2597
                        agent->systemd_dbus,
2598
                        NULL,
2599
                        SYSTEMD_BUS_NAME,
2600
                        SYSTEMD_OBJECT_PATH,
2601
                        SYSTEMD_MANAGER_IFACE,
2602
                        "UnitNew",
2603
                        agent_match_unit_new,
2604
                        agent);
2605
        if (r < 0) {
150✔
2606
                bc_log_errorf("Failed to add unit-new peer bus match: %s", strerror(-r));
×
2607
                return false;
2608
        }
2609

2610
        r = sd_bus_match_signal(
150✔
2611
                        agent->systemd_dbus,
2612
                        NULL,
2613
                        SYSTEMD_BUS_NAME,
2614
                        SYSTEMD_OBJECT_PATH,
2615
                        SYSTEMD_MANAGER_IFACE,
2616
                        "UnitRemoved",
2617
                        agent_match_unit_removed,
2618
                        agent);
2619
        if (r < 0) {
150✔
2620
                bc_log_errorf("Failed to add unit-removed peer bus match: %s", strerror(-r));
×
2621
                return false;
2622
        }
2623

2624
        r = sd_bus_match_signal(
150✔
2625
                        agent->systemd_dbus,
2626
                        NULL,
2627
                        SYSTEMD_BUS_NAME,
2628
                        SYSTEMD_OBJECT_PATH,
2629
                        SYSTEMD_MANAGER_IFACE,
2630
                        "JobRemoved",
2631
                        agent_match_job_removed,
2632
                        agent);
2633
        if (r < 0) {
150✔
2634
                bc_log_errorf("Failed to add job-removed peer bus match: %s", strerror(-r));
×
2635
                return false;
2636
        }
2637

2638
        _cleanup_sd_bus_message_ sd_bus_message *list_units_m = NULL;
150✔
2639
        r = sd_bus_call_method(
150✔
2640
                        agent->systemd_dbus,
2641
                        SYSTEMD_BUS_NAME,
2642
                        SYSTEMD_OBJECT_PATH,
2643
                        SYSTEMD_MANAGER_IFACE,
2644
                        "ListUnits",
2645
                        &error,
2646
                        &list_units_m,
2647
                        "");
2648
        if (r < 0) {
150✔
2649
                bc_log_errorf("Failed to issue list_units call: %s", error.message);
×
2650
                sd_bus_error_free(&error);
×
2651
                return false;
2652
        }
2653

2654
        r = agent_init_units(agent, list_units_m);
150✔
2655
        if (r < 0) {
150✔
2656
                return false;
2657
        }
2658

2659
        if (DEBUG_SYSTEMD_MESSAGES) {
150✔
2660
                sd_bus_add_filter(agent->systemd_dbus, NULL, debug_systemd_message_handler, agent);
2661
        }
2662

2663
        ShutdownHook hook;
150✔
2664
        hook.shutdown = (ShutdownHookFn) agent_stop;
150✔
2665
        hook.userdata = agent;
150✔
2666
        r = event_loop_add_shutdown_signals(agent->event, &hook);
150✔
2667
        if (r < 0) {
150✔
2668
                bc_log_errorf("Failed to add signals to agent event loop: %s", strerror(-r));
×
2669
                return false;
2670
        }
2671

2672
        r = agent_setup_heartbeat_timer(agent);
150✔
2673
        if (r < 0) {
150✔
2674
                bc_log_errorf("Failed to set up agent heartbeat timer: %s", strerror(-r));
×
2675
                return false;
2676
        }
2677

2678
        if (!agent_connect(agent)) {
150✔
2679
                bc_log_error("Initial controller connection failed, retrying");
×
2680
                agent->connection_state = AGENT_CONNECTION_STATE_RETRY;
×
2681
        }
2682

2683
        r = sd_event_loop(agent->event);
150✔
2684
        if (r < 0) {
150✔
2685
                bc_log_errorf("Starting event loop failed: %s", strerror(-r));
×
2686
                return false;
2687
        }
2688

2689
        return true;
2690
}
2691

2692
void agent_stop(Agent *agent) {
150✔
2693
        if (agent == NULL) {
150✔
2694
                return;
2695
        }
2696

2697
        bc_log_debug("Stopping agent");
150✔
2698

2699
        agent_peer_bus_close(agent);
150✔
2700
        agent->connection_state = AGENT_CONNECTION_STATE_DISCONNECTED;
150✔
2701
        int r = sd_bus_emit_properties_changed(
150✔
2702
                        agent->api_bus, BC_AGENT_OBJECT_PATH, AGENT_INTERFACE, "Status", NULL);
2703
        if (r < 0) {
150✔
2704
                bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
2705
        }
2706

2707
        ProxyService *proxy = NULL;
150✔
2708
        ProxyService *next_proxy = NULL;
150✔
2709
        LIST_FOREACH_SAFE(proxy_services, proxy, next_proxy, agent->proxy_services) {
152✔
2710
                agent_remove_proxy(agent, proxy, false);
2✔
2711
        }
2712
}
2713

2714
static bool agent_process_register_callback(sd_bus_message *m, Agent *agent) {
166✔
2715
        int r = 0;
166✔
2716

2717
        if (sd_bus_message_is_method_error(m, NULL)) {
166✔
2718
                bc_log_errorf("Registering as '%s' failed: %s",
15✔
2719
                              agent->name,
2720
                              sd_bus_message_get_error(m)->message);
2721
                return -EPERM;
15✔
2722
        }
2723

2724
        bc_log_info("Register call response received");
151✔
2725
        r = sd_bus_message_read(m, "");
151✔
2726
        if (r < 0) {
151✔
2727
                bc_log_errorf("Failed to parse response message: %s", strerror(-r));
×
2728
                return false;
×
2729
        }
2730

2731
        bc_log_infof("Connected to controller as '%s'", agent->name);
151✔
2732

2733
        agent->connection_state = AGENT_CONNECTION_STATE_CONNECTED;
151✔
2734
        agent->connection_retry_count = 0;
151✔
2735
        agent->controller_last_seen = get_time_micros();
151✔
2736
        agent->controller_last_seen_monotonic = get_time_micros_monotonic();
151✔
2737
        agent->disconnect_timestamp = 0;
151✔
2738
        agent->disconnect_timestamp_monotonic = 0;
151✔
2739

2740
        r = sd_bus_emit_properties_changed(
151✔
2741
                        agent->api_bus, BC_AGENT_OBJECT_PATH, AGENT_INTERFACE, "Status", NULL);
2742
        if (r < 0) {
151✔
2743
                bc_log_errorf("Failed to emit status property changed: %s", strerror(-r));
×
2744
        }
2745

2746
        r = sd_bus_match_signal(
151✔
2747
                        agent->peer_dbus,
2748
                        NULL,
2749
                        NULL,
2750
                        INTERNAL_CONTROLLER_OBJECT_PATH,
2751
                        INTERNAL_CONTROLLER_INTERFACE,
2752
                        CONTROLLER_HEARTBEAT_SIGNAL_NAME,
2753
                        agent_match_heartbeat,
2754
                        agent);
2755
        if (r < 0) {
151✔
2756
                bc_log_errorf("Failed to add heartbeat signal match: %s", strerror(-r));
×
2757
                return false;
×
2758
        }
2759

2760
        r = sd_bus_match_signal_async(
151✔
2761
                        agent->peer_dbus,
2762
                        NULL,
2763
                        "org.freedesktop.DBus.Local",
2764
                        "/org/freedesktop/DBus/Local",
2765
                        "org.freedesktop.DBus.Local",
2766
                        "Disconnected",
2767
                        agent_disconnected,
2768
                        NULL,
2769
                        agent);
2770
        if (r < 0) {
151✔
2771
                bc_log_errorf("Failed to request match for Disconnected signal: %s", strerror(-r));
×
2772
                return false;
×
2773
        }
2774

2775
        /* re-emit ProxyNew signals */
2776

2777
        ProxyService *proxy = NULL;
151✔
2778
        ProxyService *next_proxy = NULL;
151✔
2779
        LIST_FOREACH_SAFE(proxy_services, proxy, next_proxy, agent->proxy_services) {
151✔
2780
                r = proxy_service_emit_proxy_new(proxy);
×
2781
                if (r < 0) {
×
2782
                        bc_log_errorf("Failed to re-emit ProxyNew signal for proxy %u requesting unit %s on %s",
×
2783
                                      proxy->id,
2784
                                      proxy->unit_name,
2785
                                      proxy->node_name);
2786
                }
2787
        }
2788

2789
        return true;
2790
}
2791

2792
static int agent_register_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
166✔
2793
        Agent *agent = (Agent *) userdata;
166✔
2794

2795
        if (agent->connection_state != AGENT_CONNECTION_STATE_CONNECTING) {
166✔
2796
                bc_log_error("Agent is not in CONNECTING state, dropping Register callback");
×
2797
                sd_bus_slot_unrefp(&agent->register_call_slot);
×
2798
                agent->register_call_slot = NULL;
×
2799
                return 0;
×
2800
        }
2801

2802
        if (!agent_process_register_callback(m, agent)) {
166✔
2803
                agent->connection_state = AGENT_CONNECTION_STATE_RETRY;
×
2804
        }
2805

2806
        return 0;
2807
}
2808

2809
static bool agent_connect(Agent *agent) {
174✔
2810
        bc_log_infof("Connecting to controller on %s", agent->assembled_controller_address);
174✔
2811
        agent->connection_state = AGENT_CONNECTION_STATE_CONNECTING;
174✔
2812

2813
        agent->peer_dbus = peer_bus_open(
348✔
2814
                        agent->event, "peer-bus-to-controller", agent->assembled_controller_address);
174✔
2815
        if (agent->peer_dbus == NULL) {
174✔
2816
                bc_log_error("Failed to open peer dbus");
×
2817
                return false;
×
2818
        }
2819

2820
        bus_socket_set_options(agent->peer_dbus, agent->peer_socket_options);
174✔
2821

2822
        int r = sd_bus_add_object_vtable(
174✔
2823
                        agent->peer_dbus,
2824
                        NULL,
2825
                        INTERNAL_AGENT_OBJECT_PATH,
2826
                        INTERNAL_AGENT_INTERFACE,
2827
                        internal_agent_vtable,
2828
                        agent);
2829
        if (r < 0) {
174✔
2830
                bc_log_errorf("Failed to add agent vtable: %s", strerror(-r));
×
2831
                return false;
×
2832
        }
2833

2834
        _cleanup_sd_bus_message_ sd_bus_message *bus_msg = NULL;
174✔
2835
        r = sd_bus_call_method_async(
174✔
2836
                        agent->peer_dbus,
2837
                        &agent->register_call_slot,
2838
                        BC_DBUS_NAME,
2839
                        INTERNAL_CONTROLLER_OBJECT_PATH,
2840
                        INTERNAL_CONTROLLER_INTERFACE,
2841
                        "Register",
2842
                        agent_register_callback,
2843
                        agent,
2844
                        "s",
2845
                        agent->name);
2846
        if (r < 0) {
174✔
2847
                bc_log_errorf("Registering as '%s' failed: %s", agent->name, strerror(-r));
×
2848
                return false;
×
2849
        }
2850

2851
        return true;
2852
}
2853

2854
static bool agent_reconnect(Agent *agent) {
24✔
2855
        _cleanup_free_ char *assembled_controller_address = NULL;
24✔
2856

2857
        // resolve FQDN again in case the system changed
2858
        // e.g. bluechi controller has been migrated to a different host
2859
        if (agent->assembled_controller_address != NULL) {
24✔
2860
                assembled_controller_address = steal_pointer(&agent->assembled_controller_address);
24✔
2861
        }
2862
        if (!ensure_assembled_controller_address(agent)) {
24✔
2863
                return false;
2864
        }
2865

2866
        // If the controller address has changed, emit the respective signal
2867
        if (agent->assembled_controller_address && assembled_controller_address &&
24✔
2868
            !streq(agent->assembled_controller_address, assembled_controller_address)) {
24✔
2869
                int r = sd_bus_emit_properties_changed(
×
2870
                                agent->api_bus, BC_AGENT_OBJECT_PATH, AGENT_INTERFACE, "ControllerAddress", NULL);
2871
                if (r < 0) {
×
2872
                        bc_log_errorf("Failed to emit controller address property changed: %s", strerror(-r));
×
2873
                }
2874
        }
2875

2876
        agent_peer_bus_close(agent);
24✔
2877
        return agent_connect(agent);
24✔
2878
}
2879

2880
static void agent_peer_bus_close(Agent *agent) {
174✔
2881
        if (agent->register_call_slot != NULL) {
174✔
2882
                sd_bus_slot_unref(agent->register_call_slot);
167✔
2883
                agent->register_call_slot = NULL;
167✔
2884
        }
2885
        peer_bus_close(agent->peer_dbus);
174✔
2886
        agent->peer_dbus = NULL;
174✔
2887
}
174✔
2888

2889
static int stop_proxy_callback(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
3✔
2890
        UNUSED _cleanup_systemd_request_ SystemdRequest *req = userdata;
6✔
2891

2892
        if (sd_bus_message_is_method_error(m, NULL)) {
3✔
2893
                bc_log_errorf("Error stopping proxy service: %s", sd_bus_message_get_error(m)->message);
×
2894
        }
2895

2896
        return 0;
6✔
2897
}
2898

2899
/* Force stop the service via systemd */
2900
static int agent_stop_local_proxy_service(Agent *agent, ProxyService *proxy) {
10✔
2901
        if (proxy->dont_stop_proxy) {
10✔
2902
                return 0;
10✔
2903
        }
2904

2905
        bc_log_infof("Stopping proxy service %s", proxy->local_service_name);
5✔
2906

2907
        /* Don't stop twice */
2908
        proxy->dont_stop_proxy = true;
5✔
2909
        _cleanup_systemd_request_ SystemdRequest *req = agent_create_request(agent, NULL, "StopUnit");
15✔
2910
        if (req == NULL) {
5✔
2911
                return -ENOMEM;
2912
        }
2913

2914
        int r = sd_bus_message_append(req->message, "ss", proxy->local_service_name, "replace");
5✔
2915
        if (r < 0) {
5✔
2916
                return -ENOMEM;
2917
        }
2918

2919
        if (!systemd_request_start(req, stop_proxy_callback)) {
5✔
2920
                return -EIO;
×
2921
        }
2922

2923

2924
        return 0;
2925
}
2926

2927
int agent_send_job_metrics(Agent *agent, char *unit, char *method, uint64_t systemd_job_time) {
5✔
2928
        bc_log_debugf("Sending agent %s job metrics on unit %s: %ldus", unit, method, systemd_job_time);
5✔
2929
        int r = sd_bus_emit_signal(
5✔
2930
                        agent->peer_dbus,
2931
                        INTERNAL_AGENT_METRICS_OBJECT_PATH,
2932
                        INTERNAL_AGENT_METRICS_INTERFACE,
2933
                        "AgentJobMetrics",
2934
                        "sst",
2935
                        unit,
2936
                        method,
2937
                        systemd_job_time);
2938
        if (r < 0) {
5✔
2939
                bc_log_errorf("Failed to emit metric signal: %s", strerror(-r));
×
2940
        }
2941

2942
        return 0;
5✔
2943
}
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