Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hirtectl - implement getting remote unit status #350

Merged
merged 1 commit into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/man/hirtectl.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Creates a monitor on the given agent to observe changes in the specified units.

### **hirtectl** *monitor* *node-connection*

Creates a monitor to observe connection state changes for all nodes.
Creates a monitor to observe connection state changes for all nodes.


**Example:**
Expand All @@ -54,3 +54,7 @@ hirtectl monitor \\\* dbus.service,apache2.service
### **hirtectl** *daemon-reload* [*agent*]

Performs `daemon-reload` for the `hirte-agent`.

### **hirtectl** [*status*] [*agent*] [*unit1*,*...*]

Fetches the status of the systemd units for the `hirte-agent`.
10 changes: 9 additions & 1 deletion src/client/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "client.h"
#include "method_list_units.h"
#include "method_monitor.h"
#include "method_status.h"

Client *new_client(char *op, int opargc, char **opargv, char *opt_filter_glob) {
Client *client = malloc0(sizeof(Client));
Expand Down Expand Up @@ -302,7 +303,7 @@ int method_metrics_listen(Client *client) {
return r;
}

static int create_message_new_method_call(
int create_message_new_method_call(
Client *client, const char *node_name, const char *member, sd_bus_message **new_message) {
int r = 0;
_cleanup_sd_bus_message_ sd_bus_message *outgoing_message = NULL;
Expand Down Expand Up @@ -614,6 +615,11 @@ int client_call_manager(Client *client) {
return -EINVAL;
}
r = method_daemon_reload_on(client, client->opargv[0]);
} else if (streq(client->op, "status")) {
if (client->opargc < 2) {
return -EINVAL;
}
r = method_status_unit_on(client, client->opargv[0], &client->opargv[1], client->opargc - 1);
} else {
return -EINVAL;
}
Expand Down Expand Up @@ -651,5 +657,7 @@ int print_client_usage(char *argv) {
printf(" usage: monitor node-connection\n");
printf(" - daemon-reload: reload systemd daemon on a specific node\n");
printf(" usage: disable nodename\n");
printf(" - status: get the status of systemd units on a specific node\n");
printf(" usage: status nodename unitfilename...\n");
return 0;
}
3 changes: 3 additions & 0 deletions src/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ int print_client_usage(char *argv);

DEFINE_CLEANUP_FUNC(Client, client_unref)
#define _cleanup_client_ _cleanup_(client_unrefp)

int create_message_new_method_call(
Client *client, const char *node_name, const char *member, sd_bus_message **new_message);
3 changes: 2 additions & 1 deletion src/client/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ client_src = [
'main.c',
'client.c',
'method_monitor.c',
'method_list_units.c'
'method_list_units.c',
'method_status.c'
]

executable(
Expand Down
281 changes: 281 additions & 0 deletions src/client/method_status.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "libhirte/bus/utils.h"
#include "libhirte/common/common.h"

#include "client.h"
#include "method_status.h"

typedef struct unit_info_t {
const char *id;
const char *load_state;
const char *active_state;
const char *sub_state;
const char *unit_file_state;
} unit_info_t;

struct bus_properties_map {
const char *member;
size_t offset;
};

static const struct bus_properties_map property_map[] = {
{ "Id", offsetof(unit_info_t, id) },
{ "LoadState", offsetof(unit_info_t, load_state) },
{ "ActiveState", offsetof(unit_info_t, active_state) },
{ "SubState", offsetof(unit_info_t, sub_state) },
{ "UnitFileState", offsetof(unit_info_t, unit_file_state) },
{}
};

static int get_property_map(sd_bus_message *m, const struct bus_properties_map **prop) {
int r = 0;
const char *member = NULL;
int i = 0;

r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
if (r < 0) {
fprintf(stderr, "Failed to read the name of the property: %s\n", strerror(-r));
return r;
}

for (i = 0; property_map[i].member; i++) {
if (streq(property_map[i].member, member)) {
*prop = &property_map[i];
break;
}
}

return 0;
}

static int read_property_value(sd_bus_message *m, const struct bus_properties_map *prop, unit_info_t *unit_info) {
int r = 0;
char type = 0;
const char *contents = NULL;
const char *s = NULL;
const char **v = (const char **) ((uint8_t *) unit_info + prop->offset);

r = sd_bus_message_peek_type(m, NULL, &contents);
if (r < 0) {
fprintf(stderr, "Failed to peek into the type of the property: %s\n", strerror(-r));
return r;
}

r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
if (r < 0) {
fprintf(stderr, "Failed to enter into the container of the property: %s\n", strerror(-r));
return r;
}

r = sd_bus_message_peek_type(m, &type, NULL);
if (r < 0) {
fprintf(stderr, "Failed to get the type of the property: %s\n", strerror(-r));
return r;
}

if (type != SD_BUS_TYPE_STRING) {
fprintf(stderr, "Currently only string types are expected\n");
return -EINVAL;
}

r = sd_bus_message_read_basic(m, type, &s);
if (r < 0) {
fprintf(stderr, "Failed to get the value of the property: %s\n", strerror(-r));
return r;
}

if (isempty(s)) {
s = NULL;
}

*v = s;

r = sd_bus_message_exit_container(m);
if (r < 0) {
fprintf(stderr, "Failed to exit from the container of the property: %s\n", strerror(-r));
}

return r;
}

static int process_dict_entry(sd_bus_message *m, unit_info_t *unit_info) {
int r = 0;
const struct bus_properties_map *prop = NULL;

r = get_property_map(m, &prop);
if (r < 0) {
return r;
}

if (prop) {
r = read_property_value(m, prop, unit_info);
if (r < 0) {
fprintf(stderr,
"Failed to get the value for member %s - %s\n",
prop->member,
strerror(-r));
}
} else {
r = sd_bus_message_skip(m, "v");
if (r < 0) {
fprintf(stderr, "Failed to skip the property container: %s\n", strerror(-r));
}
}

return r;
}

static int parse_unit_status_response_from_message(sd_bus_message *m, unit_info_t *unit_info) {
int r = 0;

r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0) {
fprintf(stderr, "Failed to open the strings array container: %s\n", strerror(-r));
return r;
}

for (;;) {
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
if (r < 0) {
fprintf(stderr, "Failed to enter the property container: %s\n", strerror(-r));
return r;
}

if (r == 0) {
break;
}

r = process_dict_entry(m, unit_info);
if (r < 0) {
break;
}

r = sd_bus_message_exit_container(m);
if (r < 0) {
fprintf(stderr, "Failed to exit the property container: %s\n", strerror(-r));
break;
}
}

return r;
}

#define PRINT_TAB_SIZE 8
static void print_info_header(unsigned long name_col_width) {
unsigned long i = 0;

fprintf(stderr, "UNIT");
for (i = name_col_width; i > 0; i -= PRINT_TAB_SIZE) {
fprintf(stderr, "\t");
}

fprintf(stderr, "| LOADED\t| ACTIVE\t| SUBSTATE\t| ENABLED\t|\n");
for (i = name_col_width; i > 0; i -= PRINT_TAB_SIZE) {
fprintf(stderr, "--------");
}
fprintf(stderr, "----------------");
fprintf(stderr, "----------------");
fprintf(stderr, "----------------");
fprintf(stderr, "----------------\n");
}

#define PRINT_AND_ALIGN(x) \
do { \
fprintf(stderr, "| %s\t", unit_info->x); \
if (!unit_info->x || strlen(unit_info->x) < 6) { \
fprintf(stderr, "\t"); \
} \
} while (0)


static void print_unit_info(unit_info_t *unit_info, unsigned long name_col_width) {
unsigned long i = 0;

if (unit_info->load_state && streq(unit_info->load_state, "not-found")) {
fprintf(stderr, "Unit %s could not be found.\n", unit_info->id);
return;
}

fprintf(stderr, "%s", unit_info->id);
name_col_width -= unit_info->id ? strlen(unit_info->id) : 0;
name_col_width += PRINT_TAB_SIZE;
for (i = name_col_width; i > 0; i -= PRINT_TAB_SIZE) {
fprintf(stderr, "\t");
}

PRINT_AND_ALIGN(load_state);
PRINT_AND_ALIGN(active_state);
PRINT_AND_ALIGN(sub_state);
PRINT_AND_ALIGN(unit_file_state);

fprintf(stderr, "|\n");
}

static unsigned long get_max_name_len(char **units, size_t units_count) {
size_t i = 0;
unsigned long max_unit_name_len = 0;

for (i = 0; i < units_count; i++) {
unsigned long unit_name_len = strlen(units[i]);
max_unit_name_len = max_unit_name_len > unit_name_len ? max_unit_name_len : unit_name_len;
}

return max_unit_name_len;
}

static int get_status_unit_on(Client *client, char *node_name, char *unit_name, unsigned long name_col_width) {
int r = 0;
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_sd_bus_message_ sd_bus_message *result = NULL;
_cleanup_sd_bus_message_ sd_bus_message *outgoing_message = NULL;
unit_info_t unit_info = { 0 };

r = create_message_new_method_call(client, node_name, "GetUnitProperties", &outgoing_message);
if (r < 0) {
fprintf(stderr, "Failed to create a new message: %s\n", strerror(-r));
return r;
}

r = sd_bus_message_append(outgoing_message, "ss", unit_name, "");
if (r < 0) {
fprintf(stderr, "Failed to append runtime to the message: %s\n", strerror(-r));
return r;
}

r = sd_bus_call(client->api_bus, outgoing_message, HIRTE_DEFAULT_DBUS_TIMEOUT, &error, &result);
if (r < 0) {
fprintf(stderr, "Failed to issue call: %s\n", error.message);
return r;
}

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

print_unit_info(&unit_info, name_col_width);

return 0;
}

int method_status_unit_on(Client *client, char *node_name, char **units, size_t units_count) {
unsigned i = 0;

unsigned long max_name_len = get_max_name_len(units, units_count);

print_info_header(max_name_len);
for (i = 0; i < units_count; i++) {
int r = get_status_unit_on(client, node_name, units[i], max_name_len);
if (r < 0) {
fprintf(stderr,
"Failed to get status of unit %s on node %s - %s",
units[i],
node_name,
strerror(-r));
return r;
}
}

return 0;
}
6 changes: 6 additions & 0 deletions src/client/method_status.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <stddef.h>

#include "types.h"

int method_status_unit_on(Client *client, char *node_name, char **units, size_t units_count);
4 changes: 4 additions & 0 deletions src/libhirte/common/string-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ static inline bool match_glob(const char *str, const char *glob) {
return !*glob;
}
// NOLINTEND(misc-no-recursion)

static inline bool isempty(const char *a) {
return !a || a[0] == '\0';
}