]> git.meshlink.io Git - meshlink/blob - examples/manynodes.c
Add the /whitelist command to the chat examples.
[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], NULL, 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, "whitelist")) {
255                 if(!arg) {
256                         fprintf(stderr, "/whitelist requires an argument!\n");
257                         return;
258                 }
259
260                 meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
261
262                 if(!node) {
263                         fprintf(stderr, "Error looking up '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
264                         return;
265                 }
266
267                 meshlink_whitelist(mesh[nodeindex], node);
268
269                 printf("Node '%s' whitelisted.\n", arg);
270         } else if(!strcasecmp(buf, "who")) {
271                 if(!arg) {
272                         nodes = meshlink_get_all_nodes(mesh[nodeindex], nodes, &nnodes);
273
274                         if(!nodes) {
275                                 fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
276                         } else {
277                                 printf("%zu known nodes:", nnodes);
278
279                                 for(size_t i = 0; i < nnodes; i++) {
280                                         printf(" %s", nodes[i]->name);
281                                 }
282
283                                 printf("\n");
284                         }
285                 } else {
286                         meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
287
288                         if(!node) {
289                                 fprintf(stderr, "Unknown node '%s'\n", arg);
290                         } else {
291                                 printf("Node %s found, pmtu %ld\n", arg, (long int)meshlink_get_pmtu(mesh[nodeindex], node));
292                         }
293                 }
294         } else if(!strcasecmp(buf, "link")) {
295                 linkmesh();
296         } else if(!strcasecmp(buf, "eg")) {
297                 exportmeshgraph(arg);
298         } else if(!strcasecmp(buf, "egb")) {
299                 exportmeshgraph_begin(arg);
300         } else if(!strcasecmp(buf, "ege")) {
301                 exportmeshgraph_end();
302         } else if(!strcasecmp(buf, "test")) {
303                 testmesh();
304         } else if(!strcasecmp(buf, "select")) {
305                 if(!arg) {
306                         fprintf(stderr, "/select requires an argument!\n");
307                         return;
308                 }
309
310                 nodeindex = atoi(arg);
311                 printf("Index is now %d\n", nodeindex);
312         } else if(!strcasecmp(buf, "stop")) {
313                 meshlink_stop(mesh[nodeindex]);
314         } else if(!strcasecmp(buf, "quit")) {
315                 printf("Bye!\n");
316                 fclose(stdin);
317         } else if(!strcasecmp(buf, "help")) {
318                 printf(
319                         "<name>: <message>     Send a message to the given node.\n"
320                         "                      Subsequent messages don't need the <name>: prefix.\n"
321                         "/invite <name>        Create an invitation for a new node.\n"
322                         "/join <invitation>    Join an existing mesh using an invitation.\n"
323                         "/kick <name>          Blacklist the given node.\n"
324                         "/who [<name>]         List all nodes or show information about the given node.\n"
325                         "/link                 Link all nodes together.\n"
326                         "/eg <path>            Export graph as json file.\n"
327                         "/test                 Test functionality sending some data to all nodes\n"
328                         "/select <number>      Select the active node running the user commands\n"
329                         "/stop                 Call meshlink_stop, use /select first to select which node to stop\n"
330                         "/quit                 Exit this program.\n"
331                 );
332         } else {
333                 fprintf(stderr, "Unknown command '/%s'\n", buf);
334         }
335 }
336
337 static void parse_input(char *buf) {
338         static meshlink_node_t *destination;
339         size_t len;
340
341         if(!buf) {
342                 return;
343         }
344
345         // Remove newline.
346
347         len = strlen(buf);
348
349         if(len && buf[len - 1] == '\n') {
350                 buf[--len] = 0;
351         }
352
353         if(len && buf[len - 1] == '\r') {
354                 buf[--len] = 0;
355         }
356
357         // Ignore empty lines.
358
359         if(!len) {
360                 return;
361         }
362
363         // Commands start with '/'
364
365         if(*buf == '/') {
366                 parse_command(buf + 1);
367                 return;
368         }
369
370         // Lines in the form "name: message..." set the destination node.
371
372         char *msg = buf;
373         char *colon = strchr(buf, ':');
374
375         if(colon) {
376                 *colon = 0;
377                 msg = colon + 1;
378
379                 if(*msg == ' ') {
380                         msg++;
381                 }
382
383                 destination = meshlink_get_node(mesh[nodeindex], buf);
384
385                 if(!destination) {
386                         fprintf(stderr, "Unknown node '%s'\n", buf);
387                         return;
388                 }
389         }
390
391         if(!destination) {
392                 fprintf(stderr, "Who are you talking to? Write 'name: message...'\n");
393                 return;
394         }
395
396         if(!meshlink_send(mesh[nodeindex], destination, msg, strlen(msg) + 1)) {
397                 fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
398                 return;
399         }
400
401         printf("Message sent to '%s'.\n", destination->name);
402 }
403
404 int main(int argc, char *argv[]) {
405         const char *basebase = ".manynodes";
406         const char *graphexporttimeout = NULL;
407         char buf[1024];
408
409         if(argc > 1) {
410                 n = atoi(argv[1]);
411         }
412
413         if(n < 1) {
414                 fprintf(stderr, "Usage: %s [number of local nodes] [confbase] [prefixnodenames] [graphexport timeout]\n", argv[0]);
415                 return 1;
416         }
417
418         if(argc > 2) {
419                 basebase = argv[2];
420         }
421
422         if(argc > 3) {
423                 namesprefix = argv[3];
424         }
425
426         if(argc > 4) {
427                 graphexporttimeout = argv[4];
428         }
429
430         mesh = calloc(n, sizeof(*mesh));
431
432         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_message);
433 #ifndef _WIN32
434         mkdir(basebase, 0750);
435 #else
436         mkdir(basebase);
437 #endif
438
439         char filename[PATH_MAX];
440         char nodename[100];
441
442         for(int i = 0; i < n; i++) {
443                 snprintf(nodename, sizeof(nodename), "%snode%d", namesprefix, i);
444                 snprintf(filename, sizeof(filename), "%s/%s", basebase, nodename);
445
446                 if(n / (i + 1) > n / 4) {
447                         mesh[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_BACKBONE);
448                 } else {
449                         mesh[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_PORTABLE);
450                 }
451
452                 meshlink_set_log_cb(mesh[i], MESHLINK_DEBUG, log_message);
453
454                 if(!mesh[i]) {
455                         fprintf(stderr, "errno is: %d\n", meshlink_errno);
456                         fprintf(stderr, "Could not open %s: %s\n", filename, meshlink_strerror(meshlink_errno));
457                         return 1;
458                 }
459         }
460
461         int started = 0;
462
463         for(int i = 0; i < n; i++) {
464                 if(!meshlink_start(mesh[i])) {
465                         fprintf(stderr, "Could not start node %d: %s\n", i, meshlink_strerror(meshlink_errno));
466                 } else {
467                         started++;
468                 }
469         }
470
471         if(!started) {
472                 fprintf(stderr, "Could not start any node!\n");
473                 return 1;
474         }
475
476         if(graphexporttimeout) {
477                 exportmeshgraph_begin(graphexporttimeout);
478         }
479
480         printf("%d nodes started.\nType /help for a list of commands.\n", started);
481
482         // handle input
483         while(fgets(buf, sizeof(buf), stdin)) {
484                 parse_input(buf);
485         }
486
487         exportmeshgraph_end();
488
489         printf("Nodes stopping.\n");
490
491         for(int i = 0; i < n; i++) {
492                 meshlink_close(mesh[i]);
493         }
494
495         return 0;
496 }