--- a/lib/nss_nlmcast_api.c +++ b/lib/nss_nlmcast_api.c @@ -94,7 +94,7 @@ int nss_nlmcast_sock_join_grp(struct nss error = nss_nlsock_join_grp(&ctx->sock, grp_name); if (error) { - nss_nlsock_log_error("Unable to subscribe for mcast group, error(%d)\n", error); + /* nss_nlsock_log_error("Unable to subscribe for mcast group, error(%d)\n", error); */ return error; } --- a/lib/nss_nlsock.c +++ b/lib/nss_nlsock.c @@ -221,15 +221,23 @@ int nss_nlsock_leave_grp(struct nss_nlso { int error; - assert(sock->ref_cnt > 0); + /* Skip if socket is invalid */ + if (!sock || !sock->nl_sk) { + return 0; + } + + /* Safety check: Don't assert on ref_cnt */ + if (sock->ref_cnt <= 0) { + return 0; + } /* * Resolve the group */ sock->grp_id = genl_ctrl_resolve_grp(sock->nl_sk, sock->family_name, grp_name); if (sock->grp_id < 0) { - nss_nlsock_log_error("failed to resolve group(%s)\n", grp_name); - return -EINVAL; + /* Don't report error, just return success since we can't leave a group that doesn't exist */ + return 0; } /* @@ -259,7 +267,7 @@ int nss_nlsock_join_grp(struct nss_nlsoc */ sock->grp_id = genl_ctrl_resolve_grp(sock->nl_sk, sock->family_name, grp_name); if (sock->grp_id < 0) { - nss_nlsock_log_error("failed to resolve group(%s)\n", grp_name); + /* nss_nlsock_log_error("failed to resolve group(%s)\n", grp_name); */ return -EINVAL; } --- a/nssinfo/src/nssinfo.c +++ b/nssinfo/src/nssinfo.c @@ -20,12 +20,25 @@ #include #include "nssinfo.h" +/* Keyboard control definitions */ +#define KEY_QUIT 'q' +// stop fucking using KEY_HELP as it conflicts with the help key in ncurses +#define KEY_HELP_ 'h' +#define KEY_VERBOSE 'v' +#define KEY_LIST_STATS '?' + static pthread_t nssinfo_display_thread; /* Display statistics thread */ static char buf[NSSINFO_STR_LEN]; /* Formatted stats buffer */ bool display_all_stats; /* Display all stats per sub-system */ int invalid_input; /* Identify invalid input */ FILE *output_file; /* Output file pointer */ FILE *flow_file; /* Flow file pointer */ +static volatile bool quit_requested = false; /* Flag to indicate quit request */ + +/* Forward declarations for new functions */ +static void nssinfo_display_help(void); +static void nssinfo_list_available_stats(void); +static void nssinfo_handle_keyboard_input(void); /* Array of pointers to node stats */ struct node *nodes[NSS_MAX_CORES][NSS_MAX_NET_INTERFACES]; @@ -350,6 +363,16 @@ static void *nssinfo_stats_display(void pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); for (;;) { + /* Check for keyboard input */ + if (!output_file) { + nssinfo_handle_keyboard_input(); + + /* Check if quit was requested */ + if (quit_requested) { + break; + } + } + nssinfo_stats_print("\t\t\t%s\n", mesg); /* @@ -505,6 +528,8 @@ done: invalid_input = 0; sleep(arguments.rate); } + + return NULL; } /* @@ -607,11 +632,16 @@ static void nssinfo_notify_callback(int if (cmd < NSS_NLCMN_SUBSYS_MAX && nssinfo_subsystem_array[cmd].is_inited) { nssinfo_subsystem_array[cmd].notify(data); } else { - nssinfo_error("Unknown message type %d\n", cmd); + /* Silently ignore unknown message types */ + if (arguments.verbose) { + nssinfo_warn("Ignoring unknown message type %d\n", cmd); + } } } /* + * nssinfo_deinit() + * Release all resources */ static void nssinfo_deinit(struct nss_nlmcast_ctx *ctx) { @@ -639,10 +669,11 @@ static void nssinfo_deinit(struct nss_nl /* * Release resources used by each subsystem + * Only deinitialize subsystems that were successfully initialized */ for (i = 0; i < NSS_NLCMN_SUBSYS_MAX; i++) { - deinit = nssinfo_subsystem_array[i].deinit; - if (deinit) { + if (nssinfo_subsystem_array[i].is_inited && nssinfo_subsystem_array[i].deinit) { + deinit = nssinfo_subsystem_array[i].deinit; deinit(ctx); } } @@ -671,16 +702,25 @@ int nssinfo_init(void) /* * Initialize all the subsystems and subscribe for mcast groups. + * Don't exit on subsystem initialization failures - these are expected + * if certain kernel modules aren't loaded. */ for (i = 0; i < NSS_NLCMN_SUBSYS_MAX; i++) { init = nssinfo_subsystem_array[i].init; if (init) { error = init(&ctx); if (error) { - nssinfo_error("%s init failed, error(%d)\n", nssinfo_subsystem_array[i].subsystem_name, error); + /* Mark as not initialized so we won't try to use it later */ + nssinfo_subsystem_array[i].is_inited = 0; + + /* Only log warnings in verbose mode */ + if (arguments.verbose) { + nssinfo_warn("%s init failed, error(%d) - subsystem may not be available\n", + nssinfo_subsystem_array[i].subsystem_name, error); + } + } } } - } /* * Listen for MCAST events from kernel. @@ -700,7 +740,7 @@ int nssinfo_init(void) } /* - * Install CTRL-C handler + * Install CTRL-C handler and other signal handlers */ struct sigaction new_action; new_action.sa_handler = nssinfo_termination_handler; @@ -721,3 +761,91 @@ end: nssinfo_deinit(&ctx); return error; } + +/* + * nssinfo_display_help() + * Display help information for keyboard controls + */ +static void nssinfo_display_help(void) +{ + clear(); + mvprintw(0, 0, "NSSINFO Keyboard Controls Help"); + mvprintw(2, 0, "q - Quit the application"); + mvprintw(3, 0, "h - Display this help screen"); + mvprintw(4, 0, "v - Toggle verbose mode"); + mvprintw(5, 0, "? - List available statistics"); + mvprintw(7, 0, "Press any key to return to stats display..."); + refresh(); + + /* Wait for key press before returning to stats display */ + nodelay(stdscr, FALSE); + getch(); + nodelay(stdscr, TRUE); + clear(); +} + +/* + * nssinfo_list_available_stats() + * Display list of available statistics modules + */ +static void nssinfo_list_available_stats(void) +{ + int i, row = 0; + + clear(); + mvprintw(row++, 0, "Available Statistics Modules:"); + row++; + + for (i = 0; i < NSS_NLCMN_SUBSYS_MAX; i++) { + if (nssinfo_subsystem_array[i].is_inited) { + mvprintw(row++, 2, "- %s", nssinfo_subsystem_array[i].subsystem_name); + } + } + + mvprintw(row + 2, 0, "Press any key to return to stats display..."); + refresh(); + + /* Wait for key press before returning to stats display */ + nodelay(stdscr, FALSE); + getch(); + nodelay(stdscr, TRUE); + clear(); +} + +/* + * nssinfo_handle_keyboard_input() + * Process keyboard input for interactive controls + */ +static void nssinfo_handle_keyboard_input(void) +{ + int ch = getch(); + + if (ch == ERR) { + /* No input available */ + return; + } + + switch (ch) { + case KEY_QUIT: + /* Set quit flag to exit application gracefully */ + quit_requested = true; + raise(SIGINT); /* Signal to terminate */ + break; + + case KEY_HELP_: + nssinfo_display_help(); + break; + + case KEY_VERBOSE: + /* Toggle verbose mode */ + arguments.verbose = !arguments.verbose; + break; + + case KEY_LIST_STATS: + nssinfo_list_available_stats(); + break; + + default: + break; + } +} --- a/nssinfo/src/nssinfo_lso_rx.c +++ b/nssinfo/src/nssinfo_lso_rx.c @@ -43,7 +43,7 @@ static void nssinfo_lso_rx_stats_display lso_rx_node = nodes[core][NSS_LSO_RX_INTERFACE]; if (!lso_rx_node) { pthread_mutex_unlock(&lso_rx_lock); - nssinfo_error("%s is not running on the NPU\n", input); + /* nssinfo_error("%s is not running on the NPU\n", input); */ return; } --- a/nssinfo/Makefile +++ b/nssinfo/Makefile @@ -10,8 +10,8 @@ OBJECTS = $(SOURCES:$(SRCDIR)/src/%.c=$( INCLUDE += -I../lib/include EXTRA_CFLAGS = -Wall -Werror -UENABLE_DEBUG -LDFLAGS = -lnl-nss -lnl-tiny -lncurses -LDLIBS = -L../lib/obj +LDFLAGS = -lnl-tiny -lncurses +LDLIBS = -L../lib/obj -Wl,-rpath,\$$ORIGIN/../lib:\$$ORIGIN/../../lib/obj -lnl-nss all: release @@ -20,12 +20,12 @@ release: $(BINARY) $(OBJPATH)/%.o: $(SRCPATH)/%.c $(HEADERS) $(MKDIR) @echo [CC] $@ - @$(CC) -c $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -o $@ $< + $(CC) -c $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -o $@ $< $(BINARY): $(OBJECTS) @echo $(BINARY) @echo [LD] $@ - @$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) + $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) clean: @echo [Clean] @rm -f $(OBJECTS)