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