]> git.meshlink.io Git - meshlink/blob - examples/manynodes.c
manynodes: implement stop command
[meshlink] / examples / manynodes.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <linux/limits.h>
8
9 #include "../src/meshlink.h"
10 #include "../src/devtools.h"
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <errno.h>
15
16 #include <sys/time.h>
17 #include <signal.h>
18
19 static int n = 10;
20 static meshlink_handle_t **mesh;
21
22 static int nodeindex = 0;
23
24 static meshlink_node_t **nodes;
25 static size_t nnodes;
26
27 static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
28         const char *levelstr[] = {
29                 [MESHLINK_DEBUG] = "\x1b[34mDEBUG",
30                 [MESHLINK_INFO] = "\x1b[32mINFO",
31                 [MESHLINK_WARNING] = "\x1b[33mWARNING",
32                 [MESHLINK_ERROR] = "\x1b[31mERROR",
33                 [MESHLINK_CRITICAL] = "\x1b[31mCRITICAL",
34         };
35         fprintf(stderr, "%s\t%s:\x1b[0m %s\n", mesh ? mesh->name : "global",levelstr[level], text);
36 }
37
38 //Test mesh sending data
39 static void testmesh () {
40
41         for(int nindex = 0; nindex < n; nindex++) {
42
43                         nodes = meshlink_get_all_nodes(mesh[nindex], nodes, &nnodes);
44                         if(!nodes) {
45                                 fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
46                         } else {
47                                 printf("%zu known nodes:\n", nnodes);
48                                 for(int i = 0; i < nnodes; i++) {
49                                         //printf(" %s\n", nodes[i]->name);
50                                                 if(!meshlink_send(mesh[nindex], nodes[i], "magic", strlen("magic") + 1)) {
51                 fprintf(stderr, "Could not send message to '%s': %s\n", nodes[i]->name, meshlink_strerror(meshlink_errno));
52                                                 }
53                                 }
54
55                         }
56
57         }
58 }
59 // Make all nodes know about each other by importing each others public keys and addresses.
60 static void linkmesh() {
61         for(int i = 0; i < n; i++) {
62                 char *datai = meshlink_export(mesh[i]);
63
64                 for(int j = i + 1; j < n; j++) {
65                         char *dataj = meshlink_export(mesh[j]);
66                         meshlink_import(mesh[i], dataj);
67                         meshlink_import(mesh[j], datai);
68                         free(dataj);
69                 }
70
71                 free(datai);
72         }
73 }
74
75 static bool exportmeshgraph(const char* path)
76 {
77         struct stat ps;
78         int psr = stat(path, &ps);
79
80         if(psr == 0 || errno != ENOENT)
81         {
82                 if(psr == -1)
83                         { perror("stat"); }
84                 else
85                         { fprintf(stderr, "%s exists already\n", path); }
86
87                 return false;
88         }
89
90         FILE* stream = fopen(path, "w");
91
92         if(!stream)
93         {
94                 perror("stream");
95                 return false;
96         }
97
98         if(!devtool_export_json_all_edges_state(mesh[nodeindex], stream))
99         {
100                 fclose(stream);
101                 fprintf(stderr, "could not export graph\n");
102                 return false;
103         }
104
105         fclose(stream);
106         return true;
107 }
108
109
110 void exportmeshgraph_timer(int signum)
111 {
112         struct timeval ts;
113         gettimeofday(&ts, NULL);
114
115         char name[1024];
116         snprintf(name, sizeof(name), "graph_%ld_%03ld.json", ts.tv_sec, ts.tv_usec/1000);
117
118         exportmeshgraph(name);
119 }
120
121 static bool exportmeshgraph_started = false;
122
123 static bool exportmeshgraph_end(const char* none)
124 {
125         if(!exportmeshgraph_started)
126                 { return false; }
127
128         struct itimerval zero_timer = { 0 };
129         setitimer (ITIMER_REAL, &zero_timer, NULL);
130
131         exportmeshgraph_started = false;
132
133         return true;
134 }
135
136 static bool exportmeshgraph_begin(const char* timeout_str)
137 {
138         if(!timeout_str)
139                 return false;
140
141         if(exportmeshgraph_started)
142         {
143                 if(!exportmeshgraph_end(NULL))
144                         return false;
145         }
146
147         // get timeout
148         int timeout = atoi(timeout_str);
149
150         if(timeout < 100)
151                 { timeout = 100; }
152
153         int timeout_sec = timeout / 1000;
154         int timeout_msec = timeout % 1000;
155
156         /* Install timer_handler as the signal handler for SIGALRM. */
157         signal(SIGALRM, exportmeshgraph_timer);
158
159         /* Configure the timer to expire immediately... */
160         struct itimerval timer;
161         timer.it_value.tv_sec = 0;
162         timer.it_value.tv_usec = 1000;
163
164         /* ... and every X msec after that. */
165         timer.it_interval.tv_sec = timeout_sec;
166         timer.it_interval.tv_usec = timeout_msec * 1000;
167
168         /* Start a real timer. */
169         setitimer (ITIMER_REAL, &timer, NULL);
170
171         exportmeshgraph_started = true;
172
173         return true;
174 }
175
176 static void parse_command(char *buf) {
177         char *arg = strchr(buf, ' ');
178         if(arg)
179                 *arg++ = 0;
180
181         if(!strcasecmp(buf, "invite")) {
182                 char *invitation;
183
184                 if(!arg) {
185                         fprintf(stderr, "/invite requires an argument!\n");
186                         return;
187                 }
188
189                 invitation = meshlink_invite(mesh[nodeindex], arg);
190                 if(!invitation) {
191                         fprintf(stderr, "Could not invite '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
192                         return;
193                 }
194
195                 printf("Invitation for %s: %s\n", arg, invitation);
196                 free(invitation);
197         } else if(!strcasecmp(buf, "join")) {
198                 if(!arg) {
199                         fprintf(stderr, "/join requires an argument!\n");
200                         return;
201                 }
202                 meshlink_stop(mesh[nodeindex]);
203                 if(!meshlink_join(mesh[nodeindex], arg))
204                         fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
205                 else {
206                         fprintf(stderr, "Invitation accepted!\n");
207                         meshlink_start(mesh[nodeindex]);
208                 }
209         } else if(!strcasecmp(buf, "kick")) {
210                 if(!arg) {
211                         fprintf(stderr, "/kick requires an argument!\n");
212                         return;
213                 }
214
215                 meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
216                 if(!node) {
217                         fprintf(stderr, "Unknown node '%s'\n", arg);
218                         return;
219                 }
220
221                 meshlink_blacklist(mesh[nodeindex], node);
222
223                 printf("Node '%s' blacklisted.\n", arg);
224         } else if(!strcasecmp(buf, "who")) {
225                 if(!arg) {
226                         nodes = meshlink_get_all_nodes(mesh[nodeindex], nodes, &nnodes);
227                         if(!nodes) {
228                                 fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
229                         } else {
230                                 printf("%zu known nodes:", nnodes);
231                                 for(int i = 0; i < nnodes; i++)
232                                         printf(" %s", nodes[i]->name);
233                                 printf("\n");
234                         }
235                 } else {
236                         meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
237                         if(!node) {
238                                 fprintf(stderr, "Unknown node '%s'\n", arg);
239                         } else {
240                                 printf("Node %s found, pmtu %zd\n", arg, meshlink_get_pmtu(mesh[nodeindex], node));
241                         }
242                 }
243         } else if(!strcasecmp(buf, "link")) {
244                 linkmesh();
245         } else if(!strcasecmp(buf, "eg")) {
246                 exportmeshgraph(arg);
247         } else if(!strcasecmp(buf, "egb")) {
248                 exportmeshgraph_begin(arg);
249         } else if(!strcasecmp(buf, "ege")) {
250                 exportmeshgraph_end(NULL);
251         } else if(!strcasecmp(buf, "test")) {
252                 testmesh();
253         } else if(!strcasecmp(buf, "select")) {
254                 if(!arg) {
255                         fprintf(stderr, "/select requires an argument!\n");
256                         return;
257                 }
258                 nodeindex = atoi(arg);
259                 printf("Index is now %d\n",nodeindex);
260         } else if(!strcasecmp(buf, "stop")) {
261                 meshlink_stop(mesh[nodeindex]);
262         } else if(!strcasecmp(buf, "quit")) {
263                 printf("Bye!\n");
264                 fclose(stdin);
265         } else if(!strcasecmp(buf, "help")) {
266                 printf(
267                         "<name>: <message>     Send a message to the given node.\n"
268                         "                      Subsequent messages don't need the <name>: prefix.\n"
269                         "/invite <name>        Create an invitation for a new node.\n"
270                         "/join <invitation>    Join an existing mesh using an invitation.\n"
271                         "/kick <name>          Blacklist the given node.\n"
272                         "/who [<name>]         List all nodes or show information about the given node.\n"
273                         "/link                 Link all nodes together.\n"
274                         "/eg <path>            Export graph as json file.\n"
275                         "/test                 Test functionality sending some data to all nodes\n"
276                         "/select <number>      Select the active node running the user commands\n"
277                         "/stop                 Call meshlink_stop, use /select first to select which node to stop\n"
278                         "/quit                 Exit this program.\n"
279                         );
280         } else {
281                 fprintf(stderr, "Unknown command '/%s'\n", buf);
282         }
283 }
284
285 static void parse_input(char *buf) {
286         static meshlink_node_t *destination;
287         size_t len;
288
289         if(!buf)
290                 return;
291
292         // Remove newline.
293
294         len = strlen(buf);
295
296         if(len && buf[len - 1] == '\n')
297                 buf[--len] = 0;
298
299         if(len && buf[len - 1] == '\r')
300                 buf[--len] = 0;
301
302         // Ignore empty lines.
303
304         if(!len)
305                 return;
306
307         // Commands start with '/'
308
309         if(*buf == '/')
310                 return parse_command(buf + 1);
311
312         // Lines in the form "name: message..." set the destination node.
313
314         char *msg = buf;
315         char *colon = strchr(buf, ':');
316
317         if(colon) {
318                 *colon = 0;
319                 msg = colon + 1;
320                 if(*msg == ' ')
321                         msg++;
322
323                 destination = meshlink_get_node(mesh[nodeindex], buf);
324                 if(!destination) {
325                         fprintf(stderr, "Unknown node '%s'\n", buf);
326                         return;
327                 }
328         }
329
330         if(!destination) {
331                 fprintf(stderr, "Who are you talking to? Write 'name: message...'\n");
332                 return;
333         }
334
335         if(!meshlink_send(mesh[nodeindex], destination, msg, strlen(msg) + 1)) {
336                 fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
337                 return;
338         }
339
340         printf("Message sent to '%s'.\n", destination->name);
341 }
342
343 int main(int argc, char *argv[]) {
344         const char *basebase = ".manynodes";
345         const char *namesprefix = "machine1";
346         const char *graphexporttimeout = NULL;
347         char buf[1024];
348
349         if(argc > 1)
350                 n = atoi(argv[1]);
351
352         if(n < 1) {
353                 fprintf(stderr, "Usage: %s [number of local nodes] [confbase] [prefixnodenames] [graphexport timeout]\n", argv[0]);
354                 return 1;
355         }
356
357         if(argc > 2)
358                 basebase = argv[2];
359
360         if(argc > 3)
361                 namesprefix = argv[3];
362
363         if(argc > 4)
364                 graphexporttimeout = argv[4];
365
366         mesh = calloc(n, sizeof *mesh);
367
368         meshlink_set_log_cb(NULL, MESHLINK_WARNING, log_message);
369         mkdir(basebase, 0750);
370
371         char filename[PATH_MAX];
372         char nodename[100];
373         for(int i = 0; i < n; i++) {
374                 snprintf(nodename, sizeof nodename, "%snode%d", namesprefix,i);
375                 snprintf(filename, sizeof filename, "%s/%s", basebase, nodename);
376                 bool itsnew = access(filename, R_OK);
377                 mesh[i] = meshlink_open(filename, nodename, "manynodes", i%_DEV_CLASS_MAX);
378                 meshlink_set_log_cb(mesh[i], MESHLINK_WARNING, log_message);
379                 if(!mesh[i]) {
380                         fprintf(stderr, "errno is: %d\n", meshlink_errno);
381                         fprintf(stderr, "Could not open %s: %s\n", filename, meshlink_strerror(meshlink_errno));
382                         return 1;
383                 }
384         }
385
386         int started = 0;
387
388         for(int i = 0; i < n; i++) {
389                 if(!meshlink_start(mesh[i]))
390                         fprintf(stderr, "Could not start node %d: %s\n", i, meshlink_strerror(meshlink_errno));
391                 else
392                         started++;
393         }
394
395         if(!started) {
396                 fprintf(stderr, "Could not start any node!\n");
397                 return 1;
398         }
399
400         if(graphexporttimeout)
401                 { exportmeshgraph_begin(graphexporttimeout); }
402
403         printf("%d nodes started.\nType /help for a list of commands.\n", started);
404
405         // handle input
406         while(fgets(buf, sizeof buf, stdin))
407                 parse_input(buf);
408
409         exportmeshgraph_end(NULL);
410
411         printf("Nodes stopping.\n");
412
413         for(int i = 0; i < n; i++)
414                 meshlink_close(mesh[i]);
415
416         return 0;
417 }