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