]> git.meshlink.io Git - meshlink/blob - examples/monitor.c
Add an example using curses to monitor the state of MeshLink.
[meshlink] / examples / monitor.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <curses.h>
6 #include <math.h>
7 #include <assert.h>
8 #include <sys/socket.h>
9 #include <netdb.h>
10
11 #include "../src/meshlink.h"
12 #include "../src/devtools.h"
13
14 static WINDOW *topwin;
15 static WINDOW *nodewin;
16 static WINDOW *splitwin;
17 static WINDOW *logwin;
18 static WINDOW *statuswin;
19 static float splitpoint = 0.5;
20
21 static meshlink_handle_t *mesh;
22 static meshlink_node_t **nodes;
23 static size_t nnodes;
24
25 static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
26         (void)mesh;
27
28         wattron(logwin, COLOR_PAIR(level));
29         wprintw(logwin, "%s\n", text);
30         wattroff(logwin, COLOR_PAIR(level));
31         wrefresh(logwin);
32 }
33
34 static void do_resize() {
35         const int nodelines = lrintf((LINES - 3) * splitpoint);
36         const int loglines = (LINES - 3) - nodelines;
37         assert(nodelines > 1);
38         assert(loglines > 1);
39         assert(COLS > 1);
40
41         mvwin(topwin, 0, 0);
42         wresize(topwin, 1, COLS);
43
44         mvwin(nodewin, 1, 0);
45         wresize(nodewin, nodelines, COLS);
46
47         mvwin(splitwin, 1 + nodelines, 0);
48         wresize(splitwin, 1, COLS);
49
50         mvwin(logwin, 2 + nodelines, 0);
51         wresize(logwin, loglines, COLS);
52
53         mvwin(statuswin, LINES - 1, 0);
54         wresize(statuswin, 1, COLS);
55 }
56
57
58 static void do_redraw_nodes() {
59         werase(nodewin);
60         nodes = meshlink_get_all_nodes(mesh, nodes, &nnodes);
61
62         for(size_t i = 0; i < nnodes; i++) {
63                 devtool_node_status_t status;
64                 devtool_get_node_status(mesh, nodes[i], &status);
65                 char host[NI_MAXHOST] = "";
66                 char serv[NI_MAXSERV] = "";
67                 getnameinfo(&status.address, sizeof status.address, host, sizeof host, serv, sizeof serv, NI_NUMERICHOST | NI_NUMERICSERV);
68                 const char *desc;
69
70                 switch(status.udp_status) {
71                 case DEVTOOL_UDP_FAILED:
72                         desc = "UDP failed";
73                         break;
74
75                 case DEVTOOL_UDP_IMPOSSIBLE:
76                         desc = "unreachable";
77                         break;
78
79                 case DEVTOOL_UDP_TRYING:
80                         desc = "probing";
81                         break;
82
83                 case DEVTOOL_UDP_WORKING:
84                         desc = "UDP working";
85                         break;
86
87                 case DEVTOOL_UDP_UNKNOWN:
88                 default:
89                         desc = "unknown";
90                         break;
91                 };
92
93                 if(!strcmp(nodes[i]->name, mesh->name)) {
94                         desc = "myself";
95                 }
96
97                 char mtustate = ' ';
98
99                 if(status.minmtu) {
100                         if(status.minmtu != status.maxmtu) {
101                                 mtustate = '~';
102                         }
103                 };
104
105                 mvwprintw(nodewin, i, 0, "%-16s  %-12s  %-32s %5s  %c%5d", nodes[i]->name, desc, host, serv, mtustate, status.maxmtu);
106         }
107
108         wnoutrefresh(nodewin);
109 }
110
111 static void do_redraw() {
112         // Draw top line
113         werase(topwin);
114         mvwprintw(topwin, 0, 0, "%-16s  %-12s  %-32s %5s  %6s", "Node:", "Status:", "UDP address:", "Port:", "MTU:");
115         wnoutrefresh(topwin);
116
117         // Draw middle line
118         werase(splitwin);
119         mvwprintw(splitwin, 0, 0, "Log output:");
120         wnoutrefresh(splitwin);
121
122         // Draw bottom line
123         werase(statuswin);
124         mvwprintw(statuswin, 0, 0, "Status bar");
125         wnoutrefresh(statuswin);
126
127         wnoutrefresh(logwin);
128
129         do_redraw_nodes();
130 }
131
132 static void node_status(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
133         (void)mesh;
134         (void)node;
135         (void)reachable;
136         do_redraw_nodes();
137         doupdate();
138 }
139
140 int main(int argc, char *argv[]) {
141         const char *confbase = ".monitor";
142         const char *id = NULL;
143         char buf[1024];
144
145         if(argc > 1) {
146                 confbase = argv[1];
147         }
148
149         if(argc > 2) {
150                 id = argv[2];
151         }
152
153         initscr();
154         start_color();
155         curs_set(false);
156         noecho();
157
158         topwin = newwin(1, COLS, 0, 0);
159         nodewin = newwin(1, COLS, 1, 0);
160         splitwin = newwin(1, COLS, 2, 0);
161         logwin = newwin(1, COLS, 3, 0);
162         statuswin = newwin(1, COLS, 4, 0);
163
164         leaveok(topwin, true);
165         leaveok(nodewin, true);
166         leaveok(splitwin, true);
167         leaveok(logwin, true);
168         leaveok(statuswin, true);
169
170         wattrset(topwin, A_REVERSE);
171         wattrset(splitwin, A_REVERSE);
172         wattrset(statuswin, A_REVERSE);
173
174         wbkgdset(topwin, ' ' | A_REVERSE);
175         wbkgdset(splitwin, ' ' | A_REVERSE);
176         wbkgdset(statuswin, ' ' | A_REVERSE);
177
178         init_pair(1, COLOR_GREEN, -1);
179         init_pair(2, COLOR_YELLOW, -1);
180         init_pair(3, COLOR_RED, -1);
181         init_pair(4, COLOR_RED, -1);
182
183         scrollok(logwin, true);
184
185         do_resize();
186         do_redraw();
187         doupdate();
188
189         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_message);
190
191         mesh = meshlink_open(confbase, id, "monitor", DEV_CLASS_STATIONARY);
192
193         if(!mesh) {
194                 endwin();
195                 fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno));
196                 return 1;
197         }
198
199         meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_message);
200         meshlink_set_node_status_cb(mesh, node_status);
201
202         if(!meshlink_start(mesh)) {
203                 endwin();
204                 fprintf(stderr, "Could not start MeshLink: %s\n", meshlink_strerror(meshlink_errno));
205                 return 1;
206         }
207
208         bool running = true;
209         timeout(500);
210         wtimeout(topwin, 500);
211
212         do_redraw();
213         doupdate();
214
215         while(running) {
216                 int key = wgetch(topwin);
217
218                 switch(key) {
219                 case 'q':
220                 case 27:
221                 case KEY_BREAK:
222                         running = false;
223                         break;
224
225                 case KEY_RESIZE:
226                         do_resize();
227                         break;
228
229                 case 'r':
230                 case KEY_REFRESH:
231                         clearok(topwin, true);
232                         clearok(nodewin, true);
233                         clearok(splitwin, true);
234                         clearok(logwin, true);
235                         clearok(statuswin, true);
236                         break;
237                 }
238
239                 do_redraw();
240                 doupdate();
241         }
242
243         meshlink_stop(mesh);
244         meshlink_close(mesh);
245
246         endwin();
247
248         return 0;
249 }