platform/tests: extend monitor tool to dump the state of NMPlatform

This is useful for manual testing ("manual", in the sense that you can
write a script that tests the behavior of the platform cache, without
humanly reading the logfile).

Usage:

To write the content of the platform cache once:

  ./src/core/platform/tests/monitor -P -S './statefile'

To keep monitor running, and update the state file:

  ./src/core/platform/tests/monitor -S './statefile'
This commit is contained in:
Thomas Haller
2022-10-20 16:01:19 +02:00
parent 3654fc8145
commit 06cf1f5e2d

View File

@@ -8,6 +8,8 @@
#include <stdlib.h>
#include <syslog.h>
#include "libnm-glib-aux/nm-str-buf.h"
#include "libnm-glib-aux/nm-io-utils.h"
#include "libnm-platform/nm-linux-platform.h"
#include "libnm-platform/nmp-object.h"
@@ -17,10 +19,25 @@ NMTST_DEFINE();
static struct {
gboolean persist;
char *state_file;
} global_opt = {
.persist = TRUE,
};
const char *const *const ip_argv_rule6 = NM_MAKE_STRV("ip", "-d", "-6", "rule");
static const struct {
NMPObjectType obj_type;
const char *const *ip_argv;
} dump_obj_types[] = {
{NMP_OBJECT_TYPE_LINK, NM_MAKE_STRV("ip", "-d", "link")},
{NMP_OBJECT_TYPE_IP4_ADDRESS, NM_MAKE_STRV("ip", "-d", "-4", "address")},
{NMP_OBJECT_TYPE_IP6_ADDRESS, NM_MAKE_STRV("ip", "-d", "-6", "address")},
{NMP_OBJECT_TYPE_IP4_ROUTE, NM_MAKE_STRV("ip", "-d", "-4", "route")},
{NMP_OBJECT_TYPE_IP6_ROUTE, NM_MAKE_STRV("ip", "-d", "-6", "route")},
{NMP_OBJECT_TYPE_ROUTING_RULE, NM_MAKE_STRV("ip", "-d", "-4", "rule")},
};
static gboolean
read_argv(int *argc, char ***argv)
{
@@ -33,6 +50,13 @@ read_argv(int *argc, char ***argv)
&global_opt.persist,
"Exit after processing netlink messages",
NULL},
{"state-file",
'S',
0,
G_OPTION_ARG_FILENAME,
&global_opt.state_file,
N_("Dump the platform cache to this file"),
N_("FILE")},
{0},
};
gs_free_error GError *error = NULL;
@@ -63,6 +87,123 @@ mptcp_addr_dump(NMPlatform *platform)
/*****************************************************************************/
static void
_dump_state(NMPlatform *platform, const char *state_file)
{
nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE);
nm_auto_unref_gdatetime GDateTime *time_datetime = NULL;
gs_free char *time_str = NULL;
int i_obj_type;
if (!state_file)
return;
time_datetime = g_date_time_new_now_local();
time_str = g_date_time_format(time_datetime, "%C%y-%m-%dT%H:%M:%S.%f");
nm_log_dbg(LOGD_PLATFORM, "dump to file \"%s\", at %s", state_file, time_str);
nm_str_buf_append_printf(&sbuf, "time: %s\n", time_str);
nm_str_buf_append_printf(&sbuf, "pid: %lld\n", (long long) getpid());
for (i_obj_type = 0; i_obj_type < (int) G_N_ELEMENTS(dump_obj_types); i_obj_type++) {
NMPObjectType obj_type = dump_obj_types[i_obj_type].obj_type;
const char *const *ip_argv = dump_obj_types[i_obj_type].ip_argv;
const NMDedupMultiHeadEntry *pl_head_entry;
NMDedupMultiIter pl_iter;
const NMPObject *obj;
guint i;
char buf1[1000];
pl_head_entry = nm_platform_lookup_obj_type(platform, obj_type);
nm_str_buf_append_printf(&sbuf,
"\n%s: %u\n",
nmp_class_from_type(obj_type)->obj_type_name,
pl_head_entry ? pl_head_entry->len : 0u);
i = 0;
nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &obj) {
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf1, sizeof(buf1));
nm_str_buf_append_printf(&sbuf,
"%s[%u]: %s\n",
nmp_class_from_type(obj_type)->obj_type_name,
i,
buf1);
g_assert(strlen(buf1) < sizeof(buf1) - 1u);
i++;
}
ip_again:
if (ip_argv) {
gs_free_error GError *error = NULL;
gs_free char *ip_argv_ss = NULL;
gs_free char *s_stdout = NULL;
gs_free char *s_stderr = NULL;
int exit_code;
g_spawn_sync(NULL,
(char **) ip_argv,
NULL,
G_SPAWN_SEARCH_PATH,
NULL,
NULL,
&s_stdout,
&s_stderr,
&exit_code,
&error);
nm_str_buf_append_printf(&sbuf,
"%s: call %s: ",
nmp_class_from_type(obj_type)->obj_type_name,
ip_argv_ss = g_strjoinv(" ", (char **) ip_argv));
if (error) {
nm_str_buf_append_printf(&sbuf, "FAILED: %s\n", error->message);
} else if (WIFEXITED(exit_code) && WEXITSTATUS(exit_code) == 0)
nm_str_buf_append_printf(&sbuf, "SUCCESS\n");
else {
nm_str_buf_append_printf(
&sbuf,
"ERROR: %s\n",
nm_utils_get_process_exit_status_desc_buf(exit_code, buf1, sizeof(buf1)));
}
if (!nm_str_is_empty(s_stdout))
nm_str_buf_append_printf(&sbuf, "STDOUT>\n%s<\n", s_stdout);
if (!nm_str_is_empty(s_stderr))
nm_str_buf_append_printf(&sbuf, "STDERR>\n%s<\n", s_stderr);
if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE
&& ip_argv == dump_obj_types[i_obj_type].ip_argv) {
ip_argv = ip_argv_rule6;
goto ip_again;
}
}
}
nm_utils_file_set_contents(state_file,
nm_str_buf_get_str_unsafe(&sbuf),
sbuf.len,
00644,
NULL,
NULL,
NULL);
nm_log_dbg(LOGD_PLATFORM, "dump to file complete");
}
static void
_dump_state_platform_cb(NMPlatform *platform,
int obj_type_i,
int ifindex,
gconstpointer platform_object,
int change_type_i,
gpointer unused_user_data)
{
_dump_state(platform, global_opt.state_file);
}
/*****************************************************************************/
int
main(int argc, char **argv)
{
@@ -82,12 +223,32 @@ main(int argc, char **argv)
nm_linux_platform_setup();
if (global_opt.state_file) {
int i_obj_type;
for (i_obj_type = 0; i_obj_type < (int) G_N_ELEMENTS(dump_obj_types); i_obj_type++) {
NMPObjectType obj_type = dump_obj_types[i_obj_type].obj_type;
g_signal_connect(NM_PLATFORM_GET,
nmp_class_from_type(obj_type)->signal_type,
G_CALLBACK(_dump_state_platform_cb),
NULL);
}
}
mptcp_addr_dump(NM_PLATFORM_GET);
_dump_state(NM_PLATFORM_GET, global_opt.state_file);
if (global_opt.persist)
g_main_loop_run(loop);
g_main_loop_unref(loop);
g_signal_handlers_disconnect_by_func(NM_PLATFORM_GET,
G_CALLBACK(_dump_state_platform_cb),
NULL);
g_object_unref(NM_PLATFORM_GET);
return EXIT_SUCCESS;
}