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