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