]> git.meshlink.io Git - meshlink/commitdiff
Add an example using curses to monitor the state of MeshLink.
authorGuus Sliepen <guus@meshlink.io>
Tue, 9 Oct 2018 14:06:50 +0000 (16:06 +0200)
committerGuus Sliepen <guus@meshlink.io>
Tue, 9 Oct 2018 21:33:27 +0000 (23:33 +0200)
examples/Makefile.am
examples/monitor.c [new file with mode: 0644]

index 4d363657bfe1f2d754baedd7d61478d31c1310cb..9ce812d2df1287fc542f9eb41f0ed1b86398693a 100644 (file)
@@ -1,4 +1,4 @@
-noinst_PROGRAMS = meshlinkapp chat chatpp manynodes channels
+noinst_PROGRAMS = meshlinkapp chat chatpp manynodes channels monitor
 
 AM_CPPFLAGS = $(PTHREAD_CFLAGS) -I${top_srcdir}/src -iquote. -Wall
 AM_LDFLAGS = $(PTHREAD_LIBS)
@@ -17,3 +17,6 @@ manynodes_LDADD = ${top_builddir}/src/libmeshlink.la
 
 channels_SOURCES = channels.c
 channels_LDADD = ${top_builddir}/src/libmeshlink.la
+
+monitor_SOURCES = monitor.c
+monitor_LDADD = ${top_builddir}/src/libmeshlink.la -lcurses -lm
diff --git a/examples/monitor.c b/examples/monitor.c
new file mode 100644 (file)
index 0000000..ef010af
--- /dev/null
@@ -0,0 +1,249 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <curses.h>
+#include <math.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "../src/meshlink.h"
+#include "../src/devtools.h"
+
+static WINDOW *topwin;
+static WINDOW *nodewin;
+static WINDOW *splitwin;
+static WINDOW *logwin;
+static WINDOW *statuswin;
+static float splitpoint = 0.5;
+
+static meshlink_handle_t *mesh;
+static meshlink_node_t **nodes;
+static size_t nnodes;
+
+static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       (void)mesh;
+
+       wattron(logwin, COLOR_PAIR(level));
+       wprintw(logwin, "%s\n", text);
+       wattroff(logwin, COLOR_PAIR(level));
+       wrefresh(logwin);
+}
+
+static void do_resize() {
+       const int nodelines = lrintf((LINES - 3) * splitpoint);
+       const int loglines = (LINES - 3) - nodelines;
+       assert(nodelines > 1);
+       assert(loglines > 1);
+       assert(COLS > 1);
+
+       mvwin(topwin, 0, 0);
+       wresize(topwin, 1, COLS);
+
+       mvwin(nodewin, 1, 0);
+       wresize(nodewin, nodelines, COLS);
+
+       mvwin(splitwin, 1 + nodelines, 0);
+       wresize(splitwin, 1, COLS);
+
+       mvwin(logwin, 2 + nodelines, 0);
+       wresize(logwin, loglines, COLS);
+
+       mvwin(statuswin, LINES - 1, 0);
+       wresize(statuswin, 1, COLS);
+}
+
+
+static void do_redraw_nodes() {
+       werase(nodewin);
+       nodes = meshlink_get_all_nodes(mesh, nodes, &nnodes);
+
+       for(size_t i = 0; i < nnodes; i++) {
+               devtool_node_status_t status;
+               devtool_get_node_status(mesh, nodes[i], &status);
+               char host[NI_MAXHOST] = "";
+               char serv[NI_MAXSERV] = "";
+               getnameinfo(&status.address, sizeof status.address, host, sizeof host, serv, sizeof serv, NI_NUMERICHOST | NI_NUMERICSERV);
+               const char *desc;
+
+               switch(status.udp_status) {
+               case DEVTOOL_UDP_FAILED:
+                       desc = "UDP failed";
+                       break;
+
+               case DEVTOOL_UDP_IMPOSSIBLE:
+                       desc = "unreachable";
+                       break;
+
+               case DEVTOOL_UDP_TRYING:
+                       desc = "probing";
+                       break;
+
+               case DEVTOOL_UDP_WORKING:
+                       desc = "UDP working";
+                       break;
+
+               case DEVTOOL_UDP_UNKNOWN:
+               default:
+                       desc = "unknown";
+                       break;
+               };
+
+               if(!strcmp(nodes[i]->name, mesh->name)) {
+                       desc = "myself";
+               }
+
+               char mtustate = ' ';
+
+               if(status.minmtu) {
+                       if(status.minmtu != status.maxmtu) {
+                               mtustate = '~';
+                       }
+               };
+
+               mvwprintw(nodewin, i, 0, "%-16s  %-12s  %-32s %5s  %c%5d", nodes[i]->name, desc, host, serv, mtustate, status.maxmtu);
+       }
+
+       wnoutrefresh(nodewin);
+}
+
+static void do_redraw() {
+       // Draw top line
+       werase(topwin);
+       mvwprintw(topwin, 0, 0, "%-16s  %-12s  %-32s %5s  %6s", "Node:", "Status:", "UDP address:", "Port:", "MTU:");
+       wnoutrefresh(topwin);
+
+       // Draw middle line
+       werase(splitwin);
+       mvwprintw(splitwin, 0, 0, "Log output:");
+       wnoutrefresh(splitwin);
+
+       // Draw bottom line
+       werase(statuswin);
+       mvwprintw(statuswin, 0, 0, "Status bar");
+       wnoutrefresh(statuswin);
+
+       wnoutrefresh(logwin);
+
+       do_redraw_nodes();
+}
+
+static void node_status(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
+       (void)mesh;
+       (void)node;
+       (void)reachable;
+       do_redraw_nodes();
+       doupdate();
+}
+
+int main(int argc, char *argv[]) {
+       const char *confbase = ".monitor";
+       const char *id = NULL;
+       char buf[1024];
+
+       if(argc > 1) {
+               confbase = argv[1];
+       }
+
+       if(argc > 2) {
+               id = argv[2];
+       }
+
+       initscr();
+       start_color();
+       curs_set(false);
+       noecho();
+
+       topwin = newwin(1, COLS, 0, 0);
+       nodewin = newwin(1, COLS, 1, 0);
+       splitwin = newwin(1, COLS, 2, 0);
+       logwin = newwin(1, COLS, 3, 0);
+       statuswin = newwin(1, COLS, 4, 0);
+
+       leaveok(topwin, true);
+       leaveok(nodewin, true);
+       leaveok(splitwin, true);
+       leaveok(logwin, true);
+       leaveok(statuswin, true);
+
+       wattrset(topwin, A_REVERSE);
+       wattrset(splitwin, A_REVERSE);
+       wattrset(statuswin, A_REVERSE);
+
+       wbkgdset(topwin, ' ' | A_REVERSE);
+       wbkgdset(splitwin, ' ' | A_REVERSE);
+       wbkgdset(statuswin, ' ' | A_REVERSE);
+
+       init_pair(1, COLOR_GREEN, -1);
+       init_pair(2, COLOR_YELLOW, -1);
+       init_pair(3, COLOR_RED, -1);
+       init_pair(4, COLOR_RED, -1);
+
+       scrollok(logwin, true);
+
+       do_resize();
+       do_redraw();
+       doupdate();
+
+       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_message);
+
+       mesh = meshlink_open(confbase, id, "monitor", DEV_CLASS_STATIONARY);
+
+       if(!mesh) {
+               endwin();
+               fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno));
+               return 1;
+       }
+
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_message);
+       meshlink_set_node_status_cb(mesh, node_status);
+
+       if(!meshlink_start(mesh)) {
+               endwin();
+               fprintf(stderr, "Could not start MeshLink: %s\n", meshlink_strerror(meshlink_errno));
+               return 1;
+       }
+
+       bool running = true;
+       timeout(500);
+       wtimeout(topwin, 500);
+
+       do_redraw();
+       doupdate();
+
+       while(running) {
+               int key = wgetch(topwin);
+
+               switch(key) {
+               case 'q':
+               case 27:
+               case KEY_BREAK:
+                       running = false;
+                       break;
+
+               case KEY_RESIZE:
+                       do_resize();
+                       break;
+
+               case 'r':
+               case KEY_REFRESH:
+                       clearok(topwin, true);
+                       clearok(nodewin, true);
+                       clearok(splitwin, true);
+                       clearok(logwin, true);
+                       clearok(statuswin, true);
+                       break;
+               }
+
+               do_redraw();
+               doupdate();
+       }
+
+       meshlink_stop(mesh);
+       meshlink_close(mesh);
+
+       endwin();
+
+       return 0;
+}