Skip to content

Commit

Permalink
Add list-unit-files command to bluechictl
Browse files Browse the repository at this point in the history
Fixes: eclipse-bluechi#889

Signed-off-by: tallison <[email protected]>
  • Loading branch information
tallison committed Jul 23, 2024
1 parent 959bd81 commit 5cc576a
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 30 deletions.
62 changes: 32 additions & 30 deletions src/client/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "client.h"
#include "method-help.h"
#include "method-list-unit-files.h"
#include "method-list-units.h"
#include "method-loglevel.h"
#include "method-metrics.h"
Expand All @@ -33,43 +34,44 @@ int method_version(UNUSED Command *command, UNUSED void *userdata) {
}

const Method methods[] = {
{"help", 0, 0, OPT_NONE, method_help, usage_bluechi},
{ "list-units", 0, 1, OPT_FILTER, method_list_units, usage_bluechi},
{ "start", 2, 2, OPT_NONE, method_start, usage_bluechi},
{ "stop", 2, 2, OPT_NONE, method_stop, usage_bluechi},
{ "freeze", 2, 2, OPT_NONE, method_freeze, usage_bluechi},
{ "thaw", 2, 2, OPT_NONE, method_thaw, usage_bluechi},
{ "restart", 2, 2, OPT_NONE, method_restart, usage_bluechi},
{ "reload", 2, 2, OPT_NONE, method_reload, usage_bluechi},
{ "monitor", 0, 2, OPT_NONE, method_monitor, usage_bluechi},
{ "metrics", 1, 1, OPT_NONE, method_metrics, usage_bluechi},
{ "enable", 2, ARG_ANY, OPT_FORCE | OPT_RUNTIME | OPT_NO_RELOAD, method_enable, usage_bluechi},
{ "disable", 2, ARG_ANY, OPT_NO_RELOAD, method_disable, usage_bluechi},
{ "daemon-reload", 1, 1, OPT_NONE, method_daemon_reload, usage_bluechi},
{ "status", 0, ARG_ANY, OPT_WATCH, method_status, usage_bluechi},
{ "set-loglevel", 1, 2, OPT_NONE, method_set_loglevel, usage_bluechi},
{ "version", 0, 0, OPT_NONE, method_version, usage_bluechi},
{ NULL, 0, 0, 0, NULL, NULL }
{ "help", 0, 0, OPT_NONE, method_help, usage_bluechi },
{ "list-unit-files", 0, 1, OPT_FILTER, method_list_unit_files, usage_bluechi },
{ "list-units", 0, 1, OPT_FILTER, method_list_units, usage_bluechi },
{ "start", 2, 2, OPT_NONE, method_start, usage_bluechi },
{ "stop", 2, 2, OPT_NONE, method_stop, usage_bluechi },
{ "freeze", 2, 2, OPT_NONE, method_freeze, usage_bluechi },
{ "thaw", 2, 2, OPT_NONE, method_thaw, usage_bluechi },
{ "restart", 2, 2, OPT_NONE, method_restart, usage_bluechi },
{ "reload", 2, 2, OPT_NONE, method_reload, usage_bluechi },
{ "monitor", 0, 2, OPT_NONE, method_monitor, usage_bluechi },
{ "metrics", 1, 1, OPT_NONE, method_metrics, usage_bluechi },
{ "enable", 2, ARG_ANY, OPT_FORCE | OPT_RUNTIME | OPT_NO_RELOAD, method_enable, usage_bluechi },
{ "disable", 2, ARG_ANY, OPT_NO_RELOAD, method_disable, usage_bluechi },
{ "daemon-reload", 1, 1, OPT_NONE, method_daemon_reload, usage_bluechi },
{ "status", 0, ARG_ANY, OPT_WATCH, method_status, usage_bluechi },
{ "set-loglevel", 1, 2, OPT_NONE, method_set_loglevel, usage_bluechi },
{ "version", 0, 0, OPT_NONE, method_version, usage_bluechi },
{ NULL, 0, 0, 0, NULL, NULL }
};

const OptionType option_types[] = {
{ARG_FILTER_SHORT, ARG_FILTER, OPT_FILTER },
{ ARG_FORCE_SHORT, ARG_FORCE, OPT_FORCE },
{ ARG_RUNTIME_SHORT, ARG_RUNTIME, OPT_RUNTIME },
{ ARG_NO_RELOAD_SHORT, ARG_NO_RELOAD, OPT_NO_RELOAD},
{ ARG_WATCH_SHORT, ARG_WATCH, OPT_WATCH },
{ 0, NULL, 0 }
{ ARG_FILTER_SHORT, ARG_FILTER, OPT_FILTER },
{ ARG_FORCE_SHORT, ARG_FORCE, OPT_FORCE },
{ ARG_RUNTIME_SHORT, ARG_RUNTIME, OPT_RUNTIME },
{ ARG_NO_RELOAD_SHORT, ARG_NO_RELOAD, OPT_NO_RELOAD },
{ ARG_WATCH_SHORT, ARG_WATCH, OPT_WATCH },
{ 0, NULL, 0 }
};

#define GETOPT_OPTSTRING ARG_HELP_SHORT_S ARG_FORCE_SHORT_S ARG_WATCH_SHORT_S
const struct option getopt_options[] = {
{ARG_HELP, no_argument, 0, ARG_HELP_SHORT },
{ ARG_FILTER, required_argument, 0, ARG_FILTER_SHORT },
{ ARG_FORCE, no_argument, 0, ARG_FORCE_SHORT },
{ ARG_RUNTIME, no_argument, 0, ARG_RUNTIME_SHORT },
{ ARG_NO_RELOAD, no_argument, 0, ARG_NO_RELOAD_SHORT},
{ ARG_WATCH, no_argument, 0, ARG_WATCH_SHORT },
{ NULL, 0, 0, '\0' }
{ ARG_HELP, no_argument, 0, ARG_HELP_SHORT },
{ ARG_FILTER, required_argument, 0, ARG_FILTER_SHORT },
{ ARG_FORCE, no_argument, 0, ARG_FORCE_SHORT },
{ ARG_RUNTIME, no_argument, 0, ARG_RUNTIME_SHORT },
{ ARG_NO_RELOAD, no_argument, 0, ARG_NO_RELOAD_SHORT },
{ ARG_WATCH, no_argument, 0, ARG_WATCH_SHORT },
{ NULL, 0, 0, '\0' }
};

static void usage() {
Expand Down
1 change: 1 addition & 0 deletions src/client/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ client_src = [
'client.c',
'method-help.c',
'method-loglevel.c',
'method-list-unit-files.c',
'method-list-units.c',
'method-metrics.c',
'method-monitor.c',
Expand Down
2 changes: 2 additions & 0 deletions src/client/method-help.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ void usage_bluechi() {
printf("Available command:\n");
printf(" - help: shows this help message\n");
printf(" usage: help\n");
printf(" - list-unit-files: returns the list of systemd service files installed on a specific or on all nodes\n");
printf(" usage: list-unit-files [nodename] [--filter=glob]\n");
printf(" - list-units: returns the list of systemd services running on a specific or on all nodes\n");
printf(" usage: list-units [nodename] [--filter=glob]\n");
printf(" - start: starts a specific systemd service (or timer, or slice) on a specific node\n");
Expand Down
215 changes: 215 additions & 0 deletions src/client/method-list-unit-files.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright Contributors to the Eclipse BlueChi project
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "client.h"
#include "method-list-unit-files.h"

#include "libbluechi/common/math-util.h"
#include "libbluechi/common/opt.h"
#include "libbluechi/common/string-util.h"

typedef void (*print_unit_file_list_fn)(UnitFileList *unit_file_list, const char *glob_filter);

struct UnitFileList {
int ref_count;

LIST_FIELDS(UnitFileList, node_units);

LIST_HEAD(UnitFileInfo, unit_files);
};

UnitFileList *new_unit_file_list();
void unit_file_list_unref(UnitFileList *unit_file_list);

DEFINE_CLEANUP_FUNC(UnitFileList, unit_file_list_unref)
#define _cleanup_unit_file_list_ _cleanup_(unit_file_list_unrefp)

static int fetch_unit_file_list(sd_bus *api_bus,
const char *node_name,
const char *object_path,
const char *interface,
const char *typestring,
int (*parse_unit_file_info)(sd_bus_message *, UnitFileInfo *),
UnitFileList *unit_file_list) {
int r = 0;
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_sd_bus_message_ sd_bus_message *message = NULL;

r = sd_bus_call_method(
api_bus,
BC_INTERFACE_BASE_NAME,
object_path, interface,
"ListUnitFiles",
&error,
&message,
"");
if (r < 0) {
fprintf(stderr, "Failed to issue method call: %s\n", error.message);
return r;
}

r = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, typestring);
if (r < 0) {
fprintf(stderr, "Failed to read sd-bus message: %s\n", strerror(-r));
return r;
}

for (;;) {
_cleanup_unit_file_ UnitFileInfo *info = new_unit_file();

r = (*parse_unit_file_info)(message, info);
if (r < 0) {
fprintf(stderr, "Failed to parse unit file info: %s\n", strerror(-r));
return r;
}
if (r == 0) {
break;
}
if (node_name != NULL) {
info->node = strdup(node_name);
}

LIST_APPEND(unit_files, unit_file_list->unit_files, unit_file_ref(info));
}

return r;
}

static int method_list_unit_files_on_all(sd_bus *api_bus, print_unit_file_list_fn print, const char *glob_filter) {
_cleanup_unit_file_list_ UnitFileList *unit_file_list = new_unit_file_list();

int r = fetch_unit_file_list(
api_bus,
NULL,
BC_OBJECT_PATH,
CONTROLLER_INTERFACE,
"(sss)",
&bus_parse_unit_file_on_node_info,
unit_file_list);
if (r < 0) {
return r;
}

print(unit_file_list, glob_filter);

return 0;
}

static int method_list_unit_files_on(
sd_bus *api_bus, const char *node_name, print_unit_file_list_fn print, const char *glob_filter) {
int r = 0;
_cleanup_unit_file_list_ UnitFileList *unit_file_list = new_unit_file_list();

_cleanup_free_ char *object_path = NULL;
r = assemble_object_path_string(NODE_OBJECT_PATH_PREFIX, node_name, &object_path);
if (r < 0) {
return r;
}

r = fetch_unit_file_list(
api_bus,
node_name,
object_path,
NODE_INTERFACE,
"(ss)",
&bus_parse_unit_file_info,
unit_file_list);
if (r < 0) {
return r;
}

print(unit_file_list, glob_filter);

return 0;
}


/****************************
******** UnitFileList ******
****************************/

UnitFileList *new_unit_file_list() {
_cleanup_unit_file_list_ UnitFileList *unit_file_list = malloc0(sizeof(UnitFileList));

if (unit_file_list == NULL) {
return NULL;
}

unit_file_list->ref_count = 1;
LIST_HEAD_INIT(unit_file_list->unit_files);

return steal_pointer(&unit_file_list);
}

void unit_file_list_unref(UnitFileList *unit_file_list) {
unit_file_list->ref_count--;
if (unit_file_list->ref_count != 0) {
return;
}

UnitFileInfo *unit_file = NULL, *next_unit_file = NULL;
LIST_FOREACH_SAFE(unit_files, unit_file, next_unit_file, unit_file_list->unit_files) {
unit_file_unref(unit_file);
}
free(unit_file_list);
}

void print_unit_file_list_simple(UnitFileList *unit_file_list, const char *glob_filter) {
UnitFileInfo *unit_file = NULL;
const unsigned int FMT_STR_MAX_LEN = 255;
char fmt_str[FMT_STR_MAX_LEN];
const char node_title[] = "NODE";
const char path_title[] = "PATH";
const char enablment_title[] = "IS-ENABLED";
const char col_sep[] = " | ";

unsigned long max_node_len = strlen(node_title);
unsigned long max_path_len = strlen(path_title);
unsigned long max_enablement_len = strlen(enablment_title);
unsigned long sep_len = strlen(col_sep);

LIST_FOREACH(unit_files, unit_file, unit_file_list->unit_files) {
max_node_len = umaxl(max_node_len, strlen(unit_file->node));
max_path_len = umaxl(max_path_len, strlen(unit_file->unit_path));
max_enablement_len = umaxl(max_enablement_len, strlen(unit_file->enablement_status));
}

unsigned long max_line_len = max_node_len + sep_len + max_path_len + sep_len + max_enablement_len;
char sep_line[max_line_len + 1];

snprintf(fmt_str,
FMT_STR_MAX_LEN,
"%%-%lus%s%%-%lus%s%%-%lus\n",
max_node_len,
col_sep,
max_path_len,
col_sep,
max_enablement_len);

printf(fmt_str, "NODE", "PATH", "IS-ENABLED");
memset(&sep_line, '=', sizeof(char) * max_line_len);
sep_line[max_line_len] = '\0';
printf("%s\n", sep_line);

LIST_FOREACH(unit_files, unit_file, unit_file_list->unit_files) {
if (glob_filter == NULL /*|| match_glob(unit_file->)*/) {
printf(fmt_str, unit_file->node, unit_file->unit_path, unit_file->enablement_status);
}
}
}

int method_list_unit_files(Command *command, void *userdata) {
Client *client = (Client *) userdata;
char *filter_glob = command_get_option(command, ARG_FILTER_SHORT);
if (command->opargc == 0) {
return method_list_unit_files_on_all(client->api_bus, print_unit_file_list_simple, filter_glob);
}
return method_list_unit_files_on(
client->api_bus, command->opargv[0], print_unit_file_list_simple, filter_glob);
}
8 changes: 8 additions & 0 deletions src/client/method-list-unit-files.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Contributors to the Eclipse BlueChi project
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "libbluechi/cli/command.h"

int method_list_unit_files(Command *command, void *userdata);
1 change: 1 addition & 0 deletions src/client/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@

typedef struct Client Client;
typedef struct UnitList UnitList;
typedef struct UnitFileList UnitFileList;
Loading

0 comments on commit 5cc576a

Please sign in to comment.