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:
@@ -8,6 +8,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <syslog.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/nm-linux-platform.h"
|
||||||
#include "libnm-platform/nmp-object.h"
|
#include "libnm-platform/nmp-object.h"
|
||||||
|
|
||||||
@@ -17,10 +19,25 @@ NMTST_DEFINE();
|
|||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
gboolean persist;
|
gboolean persist;
|
||||||
|
char *state_file;
|
||||||
} global_opt = {
|
} global_opt = {
|
||||||
.persist = TRUE,
|
.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
|
static gboolean
|
||||||
read_argv(int *argc, char ***argv)
|
read_argv(int *argc, char ***argv)
|
||||||
{
|
{
|
||||||
@@ -33,6 +50,13 @@ read_argv(int *argc, char ***argv)
|
|||||||
&global_opt.persist,
|
&global_opt.persist,
|
||||||
"Exit after processing netlink messages",
|
"Exit after processing netlink messages",
|
||||||
NULL},
|
NULL},
|
||||||
|
{"state-file",
|
||||||
|
'S',
|
||||||
|
0,
|
||||||
|
G_OPTION_ARG_FILENAME,
|
||||||
|
&global_opt.state_file,
|
||||||
|
N_("Dump the platform cache to this file"),
|
||||||
|
N_("FILE")},
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
gs_free_error GError *error = NULL;
|
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
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -82,12 +223,32 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
nm_linux_platform_setup();
|
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);
|
mptcp_addr_dump(NM_PLATFORM_GET);
|
||||||
|
|
||||||
|
_dump_state(NM_PLATFORM_GET, global_opt.state_file);
|
||||||
|
|
||||||
if (global_opt.persist)
|
if (global_opt.persist)
|
||||||
g_main_loop_run(loop);
|
g_main_loop_run(loop);
|
||||||
|
|
||||||
g_main_loop_unref(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;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user