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

eclipse-bluechi / bluechi / 17201662698

25 Aug 2025 07:01AM UTC coverage: 20.522% (-62.2%) from 82.723%
17201662698

Pull #1094

github

web-flow
Merge 9390aa731 into 3c2c3a81a
Pull Request #1094: Add coverage improvements and version handling

1022 of 4980 relevant lines covered (20.52%)

11.12 hits per line

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

0.0
/src/agent/proxy.c
1
/*
2
 * Copyright Contributors to the Eclipse BlueChi project
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
#include "libbluechi/log/log.h"
7

8
#include "agent.h"
9
#include "proxy.h"
10

11

12
static int proxy_service_method_error(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
13
static int proxy_service_method_target_state_changed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
14
static int proxy_service_method_target_new(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
15
static int proxy_service_method_target_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
16

17
static const sd_bus_vtable proxy_service_vtable[] = {
18
        SD_BUS_VTABLE_START(0),
19
        SD_BUS_METHOD("Error", "s", "", proxy_service_method_error, 0),
20
        SD_BUS_METHOD("TargetNew", "s", "", proxy_service_method_target_new, 0),
21
        SD_BUS_METHOD("TargetStateChanged", "sss", "", proxy_service_method_target_state_changed, 0),
22
        SD_BUS_METHOD("TargetRemoved", "s", "", proxy_service_method_target_removed, 0),
23
        SD_BUS_VTABLE_END
24
};
25

26
/* This is called by the controller when the service that is the target
27
 * of a proxy changes state.
28
 */
29

30
static void proxy_service_initial_state_reached(ProxyService *proxy, bool success) {
×
31
        Agent *agent = proxy->agent;
×
32
        int r = 0;
×
33

34
        assert(proxy->request_message != NULL);
×
35

36
        if (success) {
×
37
                bc_log_infof("Replying to %s successfully", proxy->local_service_name);
×
38
                proxy->sent_successful_ready = true;
×
39
                r = sd_bus_reply_method_return(proxy->request_message, "");
×
40
        } else {
41
                bc_log_infof("Replying to %s with failure", proxy->local_service_name);
×
42
                r = sd_bus_reply_method_errorf(
×
43
                                proxy->request_message,
44
                                BC_BUS_ERROR_ACTIVATION_FAILED,
45
                                "Proxy service failed to start");
46
        }
47

48
        if (r < 0) {
×
49
                bc_log_errorf("Failed to send reply to proxy request: %s", strerror(-r));
×
50
        }
51

52
        sd_bus_message_unrefp(&proxy->request_message);
×
53
        proxy->request_message = NULL;
×
54

55
        if (!proxy->sent_successful_ready) {
×
56
                agent_remove_proxy(agent, proxy, true);
×
57
        }
58
}
×
59

60
static void proxy_service_target_stopped(ProxyService *proxy) {
×
61
        Agent *agent = proxy->agent;
×
62

63
        bc_log_infof("Proxy for %s stopping due to target stopped", proxy->local_service_name);
×
64

65
        assert(proxy->request_message == NULL);
×
66
        agent_remove_proxy(agent, proxy, true);
×
67
}
×
68

69
static int proxy_service_method_target_state_changed(
×
70
                sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
71
        _cleanup_proxy_service_ ProxyService *proxy = proxy_service_ref((ProxyService *) userdata);
×
72
        Agent *agent = proxy->agent;
×
73

74
        if (agent == NULL) {
×
75
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to get the proxy agent");
×
76
        }
77

78

79
        const char *active_state_str = NULL;
×
80
        const char *substate = NULL;
×
81
        const char *reason = NULL;
×
82
        int r = sd_bus_message_read(m, "sss", &active_state_str, &substate, &reason);
×
83
        if (r < 0) {
×
84
                return sd_bus_reply_method_errorf(
×
85
                                m,
86
                                SD_BUS_ERROR_INVALID_ARGS,
87
                                "Invalid argument for: active state, substate, or reason: %s",
88
                                strerror(-r));
89
        }
90

91
        UnitActiveState active_state = active_state_from_string(active_state_str);
×
92

93
        bc_log_debugf("Proxy service '%s' got TargetStateChanged from controller: %s %s %s",
×
94
                      proxy->local_service_name,
95
                      active_state_str,
96
                      substate,
97
                      reason);
98

99
        if (proxy->request_message) {
×
100
                /* We're waiting for the initial start-or-fail to report back
101
                 *
102
                 * There are two main things that can happen here.
103
                 *
104
                 * Either the state eventually changes to any kind of
105
                 * active state. Then we consider the target service
106
                 * to have started, and we report that, and switch to waiting
107
                 * for it to stop. It is possible that we get a virtual
108
                 * active state change because the service was already running,
109
                 * but this is fine here.
110
                 *
111
                 * Alternatively, we get a message that says the state
112
                 * switched to failed, or inactive. This means the service
113
                 * stopped, which we report as a failure to start the target.
114
                 * However, here we have to be careful about virtual inactive
115
                 * events, as we may get a virtual inactive for the current
116
                 * state before then getting a real transition to the new state.
117
                 *
118
                 * In addition, it is possible that we never ever change state, because
119
                 * the service fails to parse, for instance. If so, we hit the (real) removed
120
                 * event before seeing any activation. See below for that.
121
                 */
122

123
                if (active_state == UNIT_ACTIVE) {
×
124
                        proxy_service_initial_state_reached(proxy, true);
×
125
                } else if ((active_state == UNIT_FAILED || active_state == UNIT_INACTIVE) &&
×
126
                           streq(reason, "real")) {
×
127
                        proxy_service_initial_state_reached(proxy, false);
×
128
                }
129
        } else if (proxy->sent_successful_ready) {
×
130
                /* We reached the initial state and are now monitoring for the target to exit */
131
                if ((active_state == UNIT_FAILED || active_state == UNIT_INACTIVE) && streq(reason, "real")) {
×
132
                        proxy_service_target_stopped(proxy);
×
133
                }
134
        }
135

136
        return sd_bus_reply_method_return(m, "");
×
137
}
138

139
static int proxy_service_method_target_new(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
×
140
        _cleanup_proxy_service_ ProxyService *proxy = proxy_service_ref((ProxyService *) userdata);
×
141
        Agent *agent = proxy->agent;
×
142
        if (agent == NULL) {
×
143
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Internal error");
×
144
        }
145

146
        const char *reason = NULL;
×
147
        int r = sd_bus_message_read(m, "s", &reason);
×
148
        if (r < 0) {
×
149
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Invalid arguments");
×
150
        }
151

152
        bc_log_debugf("Proxy service '%s' got TargetNew from controller: %s", proxy->local_service_name, reason);
×
153

154
        return sd_bus_reply_method_return(m, "");
×
155
}
156

157
static int proxy_service_method_target_removed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
×
158
        _cleanup_proxy_service_ ProxyService *proxy = proxy_service_ref((ProxyService *) userdata);
×
159
        Agent *agent = proxy->agent;
×
160
        if (agent == NULL) {
×
161
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to get the proxy agent");
×
162
        }
163

164
        const char *reason = NULL;
×
165
        int r = sd_bus_message_read(m, "s", &reason);
×
166
        if (r < 0) {
×
167
                return sd_bus_reply_method_errorf(
×
168
                                m,
169
                                SD_BUS_ERROR_INVALID_ARGS,
170
                                "Invalid argument for the reason: %s",
171
                                strerror(-r));
172
        }
173

174
        bc_log_debugf("Proxy service '%s' got TargetRemoved from controller: %s",
×
175
                      proxy->local_service_name,
176
                      reason);
177

178
        /* See above in state_changed for details */
179

180
        if (proxy->request_message != NULL && streq(reason, "real")) {
×
181
                proxy_service_initial_state_reached(proxy, false);
×
182
        }
183

184
        return sd_bus_reply_method_return(m, "");
×
185
}
186

187
/* This is called by the controller when there was an error setting up the monitor.
188
 * Note, this is only sent once, and if it is sent, expect no other messages.
189
 */
190

191
static int proxy_service_method_error(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *ret_error) {
×
192
        _cleanup_proxy_service_ ProxyService *proxy = proxy_service_ref((ProxyService *) userdata);
×
193
        Agent *agent = proxy->agent;
×
194

195
        if (agent == NULL) {
×
196
                return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_FAILED, "Failed to get the proxy agent");
×
197
        }
198

199
        const char *message = NULL;
×
200
        int r = sd_bus_message_read(m, "s", &message);
×
201
        if (r < 0) {
×
202
                message = "Got no message";
×
203
        }
204

205
        bc_log_errorf("Got proxy start error: %s", message);
×
206

207
        proxy_service_initial_state_reached(proxy, false);
×
208

209
        return sd_bus_reply_method_return(m, "");
×
210
}
211

212
ProxyService *proxy_service_new(
×
213
                Agent *agent,
214
                const char *local_service_name,
215
                const char *node,
216
                const char *unit,
217
                sd_bus_message *request_message) {
218
        static uint32_t next_id = 0;
×
219

220
        _cleanup_proxy_service_ ProxyService *proxy = malloc0(sizeof(ProxyService));
×
221
        if (proxy == NULL) {
×
222
                return NULL;
223
        }
224

225
        proxy->ref_count = 1;
×
226
        proxy->id = ++next_id;
×
227
        proxy->agent = agent;
×
228
        proxy->request_message = sd_bus_message_ref(request_message);
×
229
        LIST_INIT(proxy_services, proxy);
×
230

231
        proxy->node_name = strdup(node);
×
232
        if (proxy->node_name == NULL) {
×
233
                return NULL;
234
        }
235
        proxy->unit_name = strdup(unit);
×
236
        if (proxy->unit_name == NULL) {
×
237
                return NULL;
238
        }
239
        proxy->local_service_name = strdup(local_service_name);
×
240
        if (proxy->local_service_name == NULL) {
×
241
                return NULL;
242
        }
243

244
        int r = asprintf(&proxy->object_path, "%s/%u", INTERNAL_PROXY_OBJECT_PATH_PREFIX, proxy->id);
×
245
        if (r < 0) {
×
246
                return NULL;
247
        }
248

249
        return steal_pointer(&proxy);
250
}
251

252
ProxyService *proxy_service_ref(ProxyService *proxy) {
×
253
        proxy->ref_count++;
×
254
        return proxy;
×
255
}
256

257
void proxy_service_unref(ProxyService *proxy) {
×
258
        proxy->ref_count--;
×
259
        if (proxy->ref_count != 0) {
×
260
                return;
261
        }
262

263
        /* Should have been removed from list by now */
264
        assert(proxy->proxy_services_next == NULL);
×
265
        assert(proxy->proxy_services_prev == NULL);
×
266

267
        sd_bus_message_unrefp(&proxy->request_message);
×
268
        sd_bus_slot_unrefp(&proxy->export_slot);
×
269

270
        free_and_null(proxy->node_name);
×
271
        free_and_null(proxy->unit_name);
×
272
        free_and_null(proxy->local_service_name);
×
273
        free_and_null(proxy->object_path);
×
274
        free(proxy);
×
275
}
276

277
bool proxy_service_export(ProxyService *proxy) {
×
278
        int r = sd_bus_add_object_vtable(
×
279
                        proxy->agent->peer_dbus,
×
280
                        &proxy->export_slot,
281
                        proxy->object_path,
×
282
                        INTERNAL_PROXY_INTERFACE,
283
                        proxy_service_vtable,
284
                        proxy);
285
        if (r < 0) {
×
286
                bc_log_errorf("Failed to add service proxy vtable: %s", strerror(-r));
×
287
                return false;
×
288
        }
289
        return true;
290
}
291

292
void proxy_service_unexport(ProxyService *proxy) {
×
293
        sd_bus_slot_unrefp(&proxy->export_slot);
×
294
        proxy->export_slot = NULL;
×
295
}
×
296

297
int proxy_service_emit_proxy_new(ProxyService *proxy) {
×
298
        Agent *agent = proxy->agent;
×
299

300
        if (!agent_is_connected(agent)) {
×
301
                return -ENOTCONN;
302
        }
303

304
        bc_log_infof("Registering new proxy for %s (on %s)", proxy->unit_name, proxy->node_name);
×
305
        int res = sd_bus_emit_signal(
×
306
                        agent->peer_dbus,
307
                        INTERNAL_AGENT_OBJECT_PATH,
308
                        INTERNAL_AGENT_INTERFACE,
309
                        "ProxyNew",
310
                        "sso",
311
                        proxy->node_name,
312
                        proxy->unit_name,
313
                        proxy->object_path);
314
        if (res >= 0) {
×
315
                proxy->sent_new_proxy = true;
×
316
        }
317
        return res;
318
}
319

320
int proxy_service_emit_proxy_removed(ProxyService *proxy) {
×
321
        Agent *agent = proxy->agent;
×
322

323
        /* No need to tell the controller if we didn't announce this */
324
        if (!proxy->sent_new_proxy) {
×
325
                return 0;
326
        }
327

328
        if (!agent_is_connected(agent)) {
×
329
                return -ENOTCONN;
330
        }
331

332
        bc_log_infof("Unregistering proxy for %s (on %s)", proxy->unit_name, proxy->node_name);
×
333
        return sd_bus_emit_signal(
×
334
                        proxy->agent->peer_dbus,
×
335
                        INTERNAL_AGENT_OBJECT_PATH,
336
                        INTERNAL_AGENT_INTERFACE,
337
                        "ProxyRemoved",
338
                        "ss",
339
                        proxy->node_name,
340
                        proxy->unit_name);
341
}
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