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

eclipse-bluechi / bluechi / 16743008054

05 Aug 2025 07:02AM UTC coverage: 78.083% (+7.4%) from 70.652%
16743008054

push

github

engelmi
Added pypi release section to maintainer readme

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

5369 of 6876 relevant lines covered (78.08%)

991.0 hits per line

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

58.58
/src/client/method-status.c
1
/*
2
 * Copyright Contributors to the Eclipse BlueChi project
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
#include "method-status.h"
7
#include "client.h"
8
#include "usage.h"
9

10
#include "libbluechi/bus/utils.h"
11
#include "libbluechi/common/common.h"
12
#include "libbluechi/common/opt.h"
13
#include "libbluechi/common/time-util.h"
14

15
typedef struct unit_info_t {
16
        const char *id;
17
        const char *load_state;
18
        const char *active_state;
19
        const char *freezer_state;
20
        const char *sub_state;
21
        const char *unit_file_state;
22
} unit_info_t;
23

24
struct bus_properties_map {
25
        const char *member;
26
        size_t offset;
27
};
28

29
typedef struct MethodStatusChange MethodStatusChange;
30

31
struct MethodStatusChange {
32
        Client *client;
33
        char **units;
34
        char *node_name;
35
        size_t units_count;
36
        size_t max_len;
37
};
38

39
void clear_screen() {
×
40
        printf("\033[2J");
×
41
        printf("\033[%d;%dH", 0, 0);
×
42
}
×
43

44
static const struct bus_properties_map property_map[] = {
45
        { "Id", offsetof(unit_info_t, id) },
46
        { "LoadState", offsetof(unit_info_t, load_state) },
47
        { "ActiveState", offsetof(unit_info_t, active_state) },
48
        { "FreezerState", offsetof(unit_info_t, freezer_state) },
49
        { "SubState", offsetof(unit_info_t, sub_state) },
50
        { "UnitFileState", offsetof(unit_info_t, unit_file_state) },
51
        { NULL, 0 },
52
};
53

54
static int get_property_map(sd_bus_message *m, const struct bus_properties_map **prop) {
453✔
55
        int r = 0;
453✔
56
        const char *member = NULL;
453✔
57
        int i = 0;
453✔
58

59
        r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
453✔
60
        if (r < 0) {
453✔
61
                fprintf(stderr, "Failed to read the name of the property: %s\n", strerror(-r));
×
62
                return r;
×
63
        }
64

65
        for (i = 0; property_map[i].member; i++) {
3,150✔
66
                if (streq(property_map[i].member, member)) {
2,703✔
67
                        *prop = &property_map[i];
6✔
68
                        break;
6✔
69
                }
70
        }
71

72
        return 0;
73
}
74

75
static int read_property_value(sd_bus_message *m, const struct bus_properties_map *prop, unit_info_t *unit_info) {
6✔
76
        int r = 0;
6✔
77
        char type = 0;
6✔
78
        const char *contents = NULL;
6✔
79
        const char *s = NULL;
6✔
80
        const char **v = (const char **) ((uint8_t *) unit_info + prop->offset);
6✔
81

82
        r = sd_bus_message_peek_type(m, NULL, &contents);
6✔
83
        if (r < 0) {
6✔
84
                fprintf(stderr, "Failed to peek into the type of the property: %s\n", strerror(-r));
×
85
                return r;
×
86
        }
87

88
        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
6✔
89
        if (r < 0) {
6✔
90
                fprintf(stderr, "Failed to enter into the container of the property: %s\n", strerror(-r));
×
91
                return r;
×
92
        }
93

94
        r = sd_bus_message_peek_type(m, &type, NULL);
6✔
95
        if (r < 0) {
6✔
96
                fprintf(stderr, "Failed to get the type of the property: %s\n", strerror(-r));
×
97
                return r;
×
98
        }
99

100
        if (type != SD_BUS_TYPE_STRING) {
6✔
101
                fprintf(stderr, "Currently only string types are expected\n");
×
102
                return -EINVAL;
×
103
        }
104

105
        r = sd_bus_message_read_basic(m, type, &s);
6✔
106
        if (r < 0) {
6✔
107
                fprintf(stderr, "Failed to get the value of the property: %s\n", strerror(-r));
×
108
                return r;
×
109
        }
110

111
        if (isempty(s)) {
6✔
112
                s = NULL;
×
113
        }
114

115
        *v = s;
6✔
116

117
        r = sd_bus_message_exit_container(m);
6✔
118
        if (r < 0) {
6✔
119
                fprintf(stderr, "Failed to exit from the container of the property: %s\n", strerror(-r));
×
120
        }
121

122
        return r;
123
}
124

125
static int process_dict_entry(sd_bus_message *m, unit_info_t *unit_info) {
453✔
126
        int r = 0;
453✔
127
        const struct bus_properties_map *prop = NULL;
453✔
128

129
        r = get_property_map(m, &prop);
453✔
130
        if (r < 0) {
453✔
131
                return r;
453✔
132
        }
133

134
        if (prop) {
453✔
135
                r = read_property_value(m, prop, unit_info);
6✔
136
                if (r < 0) {
6✔
137
                        fprintf(stderr,
×
138
                                "Failed to get the value for member %s - %s\n",
139
                                prop->member,
×
140
                                strerror(-r));
141
                }
142
        } else {
143
                r = sd_bus_message_skip(m, "v");
447✔
144
                if (r < 0) {
447✔
145
                        fprintf(stderr, "Failed to skip the property container: %s\n", strerror(-r));
×
146
                }
147
        }
148

149
        return r;
150
}
151

152
static int parse_unit_status_response_from_message(sd_bus_message *m, unit_info_t *unit_info) {
1✔
153
        int r = 0;
1✔
154

155
        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
1✔
156
        if (r < 0) {
1✔
157
                fprintf(stderr, "Failed to open the strings array container: %s\n", strerror(-r));
×
158
                return r;
×
159
        }
160

161
        for (;;) {
454✔
162
                r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
454✔
163
                if (r < 0) {
454✔
164
                        fprintf(stderr, "Failed to enter the property container: %s\n", strerror(-r));
×
165
                        return r;
×
166
                }
167

168
                if (r == 0) {
454✔
169
                        break;
170
                }
171

172
                r = process_dict_entry(m, unit_info);
453✔
173
                if (r < 0) {
453✔
174
                        break;
175
                }
176

177
                r = sd_bus_message_exit_container(m);
453✔
178
                if (r < 0) {
453✔
179
                        fprintf(stderr, "Failed to exit the property container: %s\n", strerror(-r));
×
180
                        break;
×
181
                }
182
        }
183

184
        return r;
185
}
186

187
#define PRINT_TAB_SIZE 8
188
static void print_info_header(size_t name_col_width) {
1✔
189
        size_t i = 0;
1✔
190

191
        fprintf(stdout, "UNIT");
1✔
192
        for (i = PRINT_TAB_SIZE + name_col_width; i > PRINT_TAB_SIZE; i -= PRINT_TAB_SIZE) {
3✔
193
                fprintf(stdout, "\t");
2✔
194
        }
195

196
        fprintf(stdout, "| LOADED\t| ACTIVE\t| SUBSTATE\t| FREEZERSTATE\t| ENABLED\t|\n");
1✔
197
        for (i = PRINT_TAB_SIZE + name_col_width; i > PRINT_TAB_SIZE; i -= PRINT_TAB_SIZE) {
4✔
198
                fprintf(stdout, "--------");
2✔
199
        }
200
        fprintf(stdout, "----------------");
1✔
201
        fprintf(stdout, "----------------");
1✔
202
        fprintf(stdout, "----------------");
1✔
203
        fprintf(stdout, "----------------");
1✔
204
        fprintf(stdout, "----------------\n");
1✔
205
}
1✔
206

207
#define PRINT_AND_ALIGN(x)                                      \
208
        do {                                                    \
209
                fprintf(stdout, "| %s\t", unit_info->x);        \
210
                if (unit_info->x && strlen(unit_info->x) < 6) { \
211
                        fprintf(stdout, "\t");                  \
212
                }                                               \
213
        } while (0)
214

215

216
static void print_unit_info(unit_info_t *unit_info, size_t name_col_width) {
1✔
217
        size_t i = 0;
1✔
218

219
        if (unit_info->load_state && streq(unit_info->load_state, "not-found")) {
1✔
220
                fprintf(stderr, "Unit %s could not be found.\n", unit_info->id);
×
221
                return;
×
222
        }
223

224
        fprintf(stdout, "%s", unit_info->id ? unit_info->id : "");
2✔
225
        name_col_width -= unit_info->id ? strlen(unit_info->id) : 0;
1✔
226
        name_col_width += PRINT_TAB_SIZE;
1✔
227
        i = name_col_width;
1✔
228
        while (i >= PRINT_TAB_SIZE) {
2✔
229
                fprintf(stdout, "\t");
1✔
230
                i -= PRINT_TAB_SIZE;
1✔
231
        }
232
        while (i > PRINT_TAB_SIZE) {
1✔
233
                fprintf(stdout, " ");
×
234
                i--;
×
235
        }
236

237

238
        PRINT_AND_ALIGN(load_state);
1✔
239
        PRINT_AND_ALIGN(active_state);
1✔
240
        PRINT_AND_ALIGN(sub_state);
1✔
241
        PRINT_AND_ALIGN(freezer_state);
1✔
242
        PRINT_AND_ALIGN(unit_file_state);
1✔
243
        fprintf(stdout, "|\n");
1✔
244

245
        fflush(stdout);
1✔
246
}
247

248
static size_t get_max_name_len(char **units, size_t units_count) {
1✔
249
        size_t i = 0;
1✔
250
        size_t max_unit_name_len = 0;
1✔
251

252
        for (i = 0; i < units_count; i++) {
2✔
253
                size_t unit_name_len = strlen(units[i]);
1✔
254
                max_unit_name_len = max_unit_name_len > unit_name_len ? max_unit_name_len : unit_name_len;
1✔
255
        }
256

257
        return max_unit_name_len;
1✔
258
}
259

260
static int get_status_unit_on(Client *client, char *node_name, char *unit_name, size_t name_col_width) {
1✔
261
        int r = 0;
1✔
262
        _cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
×
263
        _cleanup_sd_bus_message_ sd_bus_message *result = NULL;
1✔
264
        _cleanup_sd_bus_message_ sd_bus_message *outgoing_message = NULL;
1✔
265
        unit_info_t unit_info = { 0 };
1✔
266

267
        r = client_create_message_new_method_call(client, node_name, "GetUnitProperties", &outgoing_message);
1✔
268
        if (r < 0) {
1✔
269
                fprintf(stderr, "Failed to create a new message: %s\n", strerror(-r));
×
270
                return r;
271
        }
272

273
        r = sd_bus_message_append(outgoing_message, "ss", unit_name, "");
1✔
274
        if (r < 0) {
1✔
275
                fprintf(stderr, "Failed to append runtime to the message: %s\n", strerror(-r));
×
276
                return r;
277
        }
278

279
        r = sd_bus_call(client->api_bus, outgoing_message, BC_DEFAULT_DBUS_TIMEOUT, &error, &result);
1✔
280
        if (r < 0) {
1✔
281
                fprintf(stderr, "Failed to issue call: %s\n", error.message);
×
282
                return r;
283
        }
284

285
        r = parse_unit_status_response_from_message(result, &unit_info);
1✔
286
        if (r < 0) {
1✔
287
                fprintf(stderr, "Failed to parse the response strings array: %s\n", error.message);
1✔
288
                return r;
289
        }
290

291
        print_unit_info(&unit_info, name_col_width);
1✔
292

293
        return 0;
294
}
295

296

297
static int on_unit_status_changed(UNUSED sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *error) {
×
298
        MethodStatusChange *s = (MethodStatusChange *) userdata;
×
299

300
        char **units = s->units;
×
301
        char *node_name = s->node_name;
×
302
        Client *client = s->client;
×
303
        size_t units_count = s->units_count;
×
304
        size_t max_len = s->max_len;
×
305

306
        clear_screen();
×
307
        print_info_header(max_len);
×
308
        for (unsigned i = 0; i < units_count; i++) {
×
309
                int r = get_status_unit_on(client, node_name, units[i], max_len);
×
310
                if (r < 0) {
×
311
                        fprintf(stderr,
×
312
                                "Failed to get status of unit %s on node %s - %s",
313
                                units[i],
314
                                node_name,
315
                                strerror(-r));
316
                        return r;
×
317
                }
318
        }
319

320
        return 1;
321
}
322

323

324
static int method_status_unit_on(
1✔
325
                Client *client, char *node_name, char **units, size_t units_count, bool do_watch) {
326
        unsigned i = 0;
1✔
327

328
        size_t max_name_len = get_max_name_len(units, units_count);
1✔
329
        _cleanup_free_ MethodStatusChange *s = malloc0(sizeof(MethodStatusChange));
1✔
330
        if (s == NULL) {
1✔
331
                fprintf(stderr, "Failed to malloc memory for MethodStatusChange");
×
332
                return ENOMEM;
333
        }
334
        s->client = client;
1✔
335
        s->max_len = max_name_len;
1✔
336
        s->node_name = node_name;
1✔
337
        s->units = units;
1✔
338
        s->units_count = units_count;
1✔
339

340
        print_info_header(max_name_len);
1✔
341
        for (i = 0; i < units_count; i++) {
2✔
342
                int r = get_status_unit_on(client, node_name, units[i], max_name_len);
1✔
343
                if (r < 0) {
1✔
344
                        fprintf(stderr,
1✔
345
                                "Failed to get status of unit %s on node %s - %s\n",
346
                                units[i],
347
                                node_name,
348
                                strerror(-r));
349
                        return r;
350
                }
351
                if (do_watch) {
1✔
352
                        _cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
×
353
                        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
×
354
                        char *monitor_path = NULL;
×
355
                        int r = 0;
×
356

357
                        r = sd_bus_call_method(
×
358
                                        client->api_bus,
359
                                        BC_INTERFACE_BASE_NAME,
360
                                        BC_OBJECT_PATH,
361
                                        CONTROLLER_INTERFACE,
362
                                        "CreateMonitor",
363
                                        &error,
364
                                        &reply,
365
                                        "");
366
                        if (r < 0) {
×
367
                                fprintf(stderr, "Failed to create monitor: %s\n", error.message);
×
368
                                return r;
369
                        }
370

371
                        r = sd_bus_message_read(reply, "o", &monitor_path);
×
372
                        if (r < 0) {
×
373
                                fprintf(stderr,
×
374
                                        "Failed to parse create monitor response message: %s\n",
375
                                        strerror(-r));
376
                                return r;
377
                        }
378
                        r = sd_bus_match_signal(
×
379
                                        client->api_bus,
380
                                        NULL,
381
                                        BC_INTERFACE_BASE_NAME,
382
                                        monitor_path,
383
                                        MONITOR_INTERFACE,
384
                                        "UnitStateChanged",
385
                                        on_unit_status_changed,
386
                                        s);
387
                        if (r < 0) {
×
388
                                fprintf(stderr,
×
389
                                        "Failed to create callback for UnitStateChanged: %s\n",
390
                                        strerror(-r));
391
                                return r;
392
                        }
393

394
                        _cleanup_sd_bus_message_ sd_bus_message *m = NULL;
×
395
                        r = sd_bus_message_new_method_call(
×
396
                                        client->api_bus,
397
                                        &m,
398
                                        BC_INTERFACE_BASE_NAME,
399
                                        monitor_path,
400
                                        MONITOR_INTERFACE,
401
                                        "Subscribe");
402
                        if (r < 0) {
×
403
                                fprintf(stderr, "Failed creating subscription call: %s\n", strerror(-r));
×
404
                                return r;
405
                        }
406
                        sd_bus_message_append(m, "ss", node_name, units[i]);
×
407
                        _cleanup_sd_bus_message_ sd_bus_message *subscription_reply = NULL;
×
408
                        r = sd_bus_call(client->api_bus, m, BC_DEFAULT_DBUS_TIMEOUT, &error, &subscription_reply);
×
409
                        if (r < 0) {
×
410
                                fprintf(stderr, "Failed to subscribe to monitor: %s\n", error.message);
×
411
                                return r;
×
412
                        }
413
                }
414
        }
415

416
        return do_watch ? client_start_event_loop(client) : 0;
1✔
417
}
418

419
/***************************************************************
420
 ******** Monitor: Connection state changes of agents **********
421
 ***************************************************************/
422

423
typedef struct Node Node;
424
typedef struct Nodes Nodes;
425
typedef struct NodeConnection NodeConnection;
426

427
struct NodeConnection {
428
        char *name;
429
        char *node_path;
430
        char *state;
431
        char *ip;
432
        uint64_t last_seen;
433
};
434

435
typedef struct Node {
436
        sd_bus *api_bus;
437
        NodeConnection *connection;
438
        LIST_FIELDS(Node, nodes);
439
        Nodes *nodes;
440
} Node;
441

442
typedef struct Nodes {
443
        LIST_HEAD(Node, nodes);
444
} Nodes;
445

446
static Node *
447
                node_new(sd_bus *api_bus,
10✔
448
                         Nodes *head,
449
                         const char *node_name,
450
                         const char *node_path,
451
                         const char *node_state,
452
                         const char *ip,
453
                         uint64_t last_seen_timestamp) {
454
        Node *node = malloc0(sizeof(Node));
10✔
455
        if (node == NULL) {
10✔
456
                return NULL;
10✔
457
        }
458
        node->connection = malloc0(sizeof(NodeConnection));
10✔
459
        if (node->connection == NULL) {
10✔
460
                free(node);
×
461
                node = NULL;
×
462
                return NULL;
×
463
        }
464

465
        _cleanup_free_ char *name_dup = strdup(node_name);
20✔
466
        _cleanup_free_ char *node_path_dup = strdup(node_path);
20✔
467
        _cleanup_free_ char *state_dup = strdup(node_state);
20✔
468
        _cleanup_free_ char *ip_dup = strdup(ip);
10✔
469
        if (name_dup == NULL || node_path_dup == NULL || state_dup == NULL || ip_dup == NULL) {
10✔
470
                free(node->connection);
×
471
                node->connection = NULL;
×
472
                free(node);
×
473
                node = NULL;
×
474
                return NULL;
×
475
        }
476
        node->connection->name = steal_pointer(&name_dup);
10✔
477
        node->connection->node_path = steal_pointer(&node_path_dup);
10✔
478
        node->connection->state = steal_pointer(&state_dup);
10✔
479
        node->connection->ip = steal_pointer(&ip_dup);
10✔
480
        node->connection->last_seen = last_seen_timestamp;
10✔
481
        node->api_bus = api_bus;
10✔
482
        node->nodes = head;
10✔
483
        LIST_APPEND(nodes, head->nodes, node);
10✔
484
        return node;
485
}
486

487
static Nodes *nodes_new() {
8✔
488
        Nodes *nodes = malloc0(sizeof(Nodes));
8✔
489
        if (nodes == NULL) {
8✔
490
                return NULL;
491
        }
492
        LIST_HEAD_INIT(nodes->nodes);
8✔
493
        return nodes;
8✔
494
}
495

496
static char *node_connection_fmt_last_seen(NodeConnection *con) {
10✔
497
        if (streq(con->state, "online")) {
10✔
498
                return strdup("now");
5✔
499
        } else if (streq(con->state, "offline") && con->last_seen == 0) {
5✔
500
                return strdup("never");
4✔
501
        }
502

503
        struct timespec t;
1✔
504
        t.tv_sec = (time_t) con->last_seen / USEC_PER_SEC;
1✔
505
        t.tv_nsec = (long) (con->last_seen % USEC_PER_SEC) * NSEC_PER_USEC;
1✔
506
        return get_formatted_log_timestamp_for_timespec(t, false);
1✔
507
}
508

509
static void node_unref(Node *node) {
10✔
510
        if (node == NULL) {
10✔
511
                return;
512
        }
513

514
        if (node->connection != NULL) {
10✔
515
                free_and_null(node->connection->name);
10✔
516
                free_and_null(node->connection->node_path);
10✔
517
                free_and_null(node->connection->state);
10✔
518
                free_and_null(node->connection->ip);
10✔
519
                free_and_null(node->connection);
10✔
520
        }
521

522
        if (node->nodes != NULL) {
10✔
523
                node->nodes = NULL;
10✔
524
        }
525

526
        free(node);
10✔
527
        node = NULL;
10✔
528
}
529

530

531
static void nodes_unref(Nodes *nodes) {
8✔
532
        if (nodes == NULL) {
8✔
533
                return;
534
        }
535

536
        Node *curr = NULL;
8✔
537
        Node *next = NULL;
8✔
538
        LIST_FOREACH_SAFE(nodes, curr, next, nodes->nodes) {
18✔
539
                node_unref(curr);
10✔
540
        }
541

542
        free(nodes);
8✔
543
        nodes = NULL;
8✔
544
}
545

546
DEFINE_CLEANUP_FUNC(Nodes, nodes_unref)
8✔
547
#define _cleanup_nodes_ _cleanup_(nodes_unrefp)
548

549
static void print_nodes(Nodes *nodes, bool clear_screen) {
8✔
550
        if (clear_screen) {
8✔
551
                printf("\033[2J");
×
552
                printf("\033[%d;%dH", 0, 0);
×
553
        }
554

555
        /* print monitor header */
556
        printf("%-30.30s| %-10.10s| %-24.24s| %-28.28s\n", "NODE", "STATE", "IP", "LAST SEEN");
8✔
557
        printf("==========================================================================================\n");
8✔
558

559
        Node *curr = NULL;
8✔
560
        Node *next = NULL;
8✔
561
        LIST_FOREACH_SAFE(nodes, curr, next, nodes->nodes) {
18✔
562
                _cleanup_free_ char *last_seen = node_connection_fmt_last_seen(curr->connection);
20✔
563
                printf("%-30.30s| %-10.10s| %-24.24s| %-28.28s\n",
20✔
564
                       curr->connection->name,
565
                       curr->connection->state,
566
                       curr->connection->ip,
10✔
567
                       last_seen);
568
                fflush(stdout);
10✔
569
        }
570
}
8✔
571

572

573
static int fetch_last_seen_timestamp_property(
5✔
574
                sd_bus *api_bus, const char *node_path, uint64_t *ret_last_seen_timestamp) {
575
        _cleanup_sd_bus_error_ sd_bus_error prop_error = SD_BUS_ERROR_NULL;
×
576
        _cleanup_sd_bus_message_ sd_bus_message *prop_reply = NULL;
5✔
577

578
        int r = sd_bus_get_property(
5✔
579
                        api_bus,
580
                        BC_INTERFACE_BASE_NAME,
581
                        node_path,
582
                        NODE_INTERFACE,
583
                        "LastSeenTimestamp",
584
                        &prop_error,
585
                        &prop_reply,
586
                        "t");
587
        if (r < 0) {
5✔
588
                return r;
589
        }
590

591
        uint64_t last_seen_timestamp = 0;
5✔
592
        r = sd_bus_message_read(prop_reply, "t", &last_seen_timestamp);
5✔
593
        if (r < 0) {
5✔
594
                return r;
595
        }
596
        *ret_last_seen_timestamp = last_seen_timestamp;
5✔
597

598
        return 0;
5✔
599
}
600

601
static int fetch_peer_ip_property(sd_bus *api_bus, const char *node_path, char **ret_peer_ip) {
×
602
        _cleanup_sd_bus_error_ sd_bus_error prop_error = SD_BUS_ERROR_NULL;
×
603
        _cleanup_sd_bus_message_ sd_bus_message *prop_reply = NULL;
×
604

605
        int r = sd_bus_get_property(
×
606
                        api_bus,
607
                        BC_INTERFACE_BASE_NAME,
608
                        node_path,
609
                        NODE_INTERFACE,
610
                        "PeerIp",
611
                        &prop_error,
612
                        &prop_reply,
613
                        "s");
614
        if (r < 0) {
×
615
                return r;
616
        }
617

618
        const char *peer_ip = NULL;
×
619
        r = sd_bus_message_read(prop_reply, "s", &peer_ip);
×
620
        if (r < 0) {
×
621
                return r;
622
        }
623
        copy_str(ret_peer_ip, peer_ip);
×
624

625
        return 0;
×
626
}
627

628
static int parse_status_from_changed_properties(sd_bus_message *m, char **ret_connection_status) {
×
629
        if (ret_connection_status == NULL) {
×
630
                fprintf(stderr, "NULL pointer to connection status not allowed");
×
631
                return -EINVAL;
×
632
        }
633

634
        int r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
×
635
        if (r < 0) {
×
636
                fprintf(stderr, "Failed to read changed properties: %s\n", strerror(-r));
×
637
                return r;
×
638
        }
639

640
        for (;;) {
×
641
                r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
×
642
                if (r <= 0) {
×
643
                        break;
644
                }
645

646
                const char *key = NULL;
×
647
                r = sd_bus_message_read(m, "s", &key);
×
648
                if (r < 0) {
×
649
                        fprintf(stderr, "Failed to read next unit changed property: %s\n", strerror(-r));
×
650
                        return r;
×
651
                }
652
                if (r == 0) {
×
653
                        break;
654
                }
655

656
                /* only process property changes for the node status */
657
                if (!streq(key, "Status")) {
×
658
                        break;
659
                }
660

661
                /* node status is always of type string */
662
                r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "s");
×
663
                if (r < 0) {
×
664
                        fprintf(stderr, "Failed to enter content of variant type: %s", strerror(-r));
×
665
                        return r;
×
666
                }
667
                char *status = NULL;
×
668
                r = sd_bus_message_read(m, "s", &status);
×
669
                if (r < 0) {
×
670
                        fprintf(stderr, "Failed to read value of changed property: %s\n", strerror(-r));
×
671
                        return r;
×
672
                }
673
                *ret_connection_status = strdup(status);
×
674

675
                r = sd_bus_message_exit_container(m);
×
676
                if (r < 0) {
×
677
                        fprintf(stderr, "Failed to exit container: %s\n", strerror(-r));
×
678
                        return r;
×
679
                }
680
        }
681

682
        return sd_bus_message_exit_container(m);
×
683
}
684

685
static int on_node_connection_state_changed(sd_bus_message *m, void *userdata, UNUSED sd_bus_error *error) {
×
686
        Node *node = userdata;
×
687

688
        (void) sd_bus_message_skip(m, "s");
×
689

690
        _cleanup_free_ char *con_state = NULL;
×
691
        int r = parse_status_from_changed_properties(m, &con_state);
×
692
        if (r < 0) {
×
693
                return r;
694
        }
695
        if (con_state == NULL) {
×
696
                fprintf(stderr, "Received connection status change signal with missing 'Status' key.");
×
697
                return 0;
698
        }
699

700
        r = fetch_last_seen_timestamp_property(
×
701
                        node->api_bus, node->connection->node_path, &node->connection->last_seen);
×
702
        if (r < 0) {
×
703
                fprintf(stderr,
×
704
                        "Failed to get last seen property of node %s: %s\n",
705
                        node->connection->name,
×
706
                        strerror(-r));
707
                return r;
708
        }
709
        r = fetch_peer_ip_property(node->api_bus, node->connection->node_path, &node->connection->ip);
×
710
        if (r < 0) {
×
711
                fprintf(stderr,
×
712
                        "Failed to get peer IP property of node %s: %s\n",
713
                        node->connection->name,
×
714
                        strerror(-r));
715
                return r;
716
        }
717

718
        free(node->connection->state);
×
719
        node->connection->state = strdup(con_state);
×
720

721
        print_nodes(node->nodes, true);
×
722

723
        return 0;
724
}
725

726
static int method_print_node_status(Client *client, char *node_name, bool do_watch) {
8✔
727
        _cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
×
728
        _cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
8✔
729
        int r = 0;
8✔
730

731
        r = sd_bus_call_method(
8✔
732
                        client->api_bus,
733
                        BC_INTERFACE_BASE_NAME,
734
                        BC_OBJECT_PATH,
735
                        CONTROLLER_INTERFACE,
736
                        "ListNodes",
737
                        &error,
738
                        &reply,
739
                        "");
740
        if (r < 0) {
8✔
741
                fprintf(stderr, "Failed to list nodes: %s\n", error.message);
×
742
                return r;
743
        }
744

745
        _cleanup_nodes_ Nodes *nodes = nodes_new();
16✔
746
        if (nodes == NULL) {
8✔
747
                fprintf(stderr, "Failed to create Node list, OOM");
×
748
                return -ENOMEM;
749
        }
750

751
        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(soss)");
8✔
752
        if (r < 0) {
8✔
753
                fprintf(stderr, "Failed to open reply array: %s\n", strerror(-r));
×
754
                return r;
755
        }
756
        while (sd_bus_message_at_end(reply, false) == 0) {
24✔
757
                const char *name = NULL;
16✔
758
                const char *path = NULL;
16✔
759
                const char *state = NULL;
16✔
760
                const char *ip = NULL;
16✔
761
                uint64_t last_seen_timestamp = 0;
16✔
762

763
                r = sd_bus_message_read(reply, "(soss)", &name, &path, &state, &ip);
16✔
764
                if (r < 0) {
16✔
765
                        fprintf(stderr, "Failed to read node information: %s\n", strerror(-r));
×
766
                        return r;
×
767
                }
768

769
                if (node_name != NULL && !streq(name, node_name)) {
16✔
770
                        continue;
6✔
771
                }
772

773
                if (streq(state, "offline")) {
10✔
774
                        r = fetch_last_seen_timestamp_property(client->api_bus, path, &last_seen_timestamp);
5✔
775
                        if (r < 0) {
5✔
776
                                fprintf(stderr,
×
777
                                        "Failed to get last seen property of node %s: %s\n",
778
                                        name,
779
                                        strerror(-r));
780
                                return r;
781
                        }
782
                }
783

784
                Node *node = node_new(client->api_bus, nodes, name, path, state, ip, last_seen_timestamp);
10✔
785
                if (node == NULL) {
10✔
786
                        fprintf(stderr, "Failed to create Node, OOM");
×
787
                        return -ENOMEM;
788
                }
789
                if (do_watch) {
10✔
790
                        // Create a callback for state change monitoring
791
                        r = sd_bus_match_signal(
×
792
                                        client->api_bus,
793
                                        NULL,
794
                                        BC_INTERFACE_BASE_NAME,
795
                                        path,
796
                                        "org.freedesktop.DBus.Properties",
797
                                        "PropertiesChanged",
798
                                        on_node_connection_state_changed,
799
                                        node);
800
                        if (r < 0) {
×
801
                                fprintf(stderr,
×
802
                                        "Failed to create callback for NodeConnectionStateChanged: %s\n",
803
                                        strerror(-r));
804
                                return r;
805
                        }
806
                }
807
        }
808

809
        if (node_name != NULL && LIST_IS_EMPTY(nodes->nodes)) {
8✔
810
                fprintf(stderr, "Node %s not found\n", node_name);
8✔
811
                return -EINVAL;
812
        }
813

814
        print_nodes(nodes, do_watch);
8✔
815

816
        return do_watch ? client_start_event_loop(client) : 0;
8✔
817
}
818

819
int method_status(Command *command, void *userdata) {
9✔
820
        switch (command->opargc) {
9✔
821
        case 0:
2✔
822
                return method_print_node_status(userdata, NULL, command_flag_exists(command, ARG_WATCH_SHORT));
2✔
823
        case 1:
6✔
824
                return method_print_node_status(
12✔
825
                                userdata, command->opargv[0], command_flag_exists(command, ARG_WATCH_SHORT));
6✔
826
        default:
1✔
827
                return method_status_unit_on(
2✔
828
                                userdata,
829
                                command->opargv[0],
830
                                &command->opargv[1],
1✔
831
                                command->opargc - 1,
1✔
832
                                command_flag_exists(command, ARG_WATCH_SHORT));
1✔
833
        }
834
}
835

836

837
void usage_method_status() {
×
838
        usage_print_header();
×
839
        usage_print_description("View status for units and nodes of BlueChi");
×
840
        usage_print_usage("bluechictl status [nodename] [unitname] [options]");
×
841
        printf("  If [nodename] and [unitname] are not given, the status of all nodes will be displayed.\n");
×
842
        printf("  If [unitname] is not given, the status of the specific node will be displayed.\n");
×
843
        printf("\n");
×
844
        printf("Available options:\n");
×
845
        printf("  --%s \t shows this help message\n", ARG_HELP);
×
846
        printf("  --%s, -%c \t continuously watch the status of the node(s) and unit \n",
×
847
               ARG_WATCH,
848
               ARG_WATCH_SHORT);
849
}
×
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