]> git.meshlink.io Git - meshlink-tiny/blob - examples/manynodes.c
Fix building the example programs.
[meshlink-tiny] / 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-tiny.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, "join")) {
206                 if(!arg) {
207                         fprintf(stderr, "/join requires an argument!\n");
208                         return;
209                 }
210
211                 meshlink_stop(meshes[nodeindex]);
212
213                 if(!meshlink_join(meshes[nodeindex], arg)) {
214                         fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
215                 } else {
216                         fprintf(stderr, "Invitation accepted!\n");
217                 }
218
219                 if(!meshlink_start(meshes[nodeindex])) {
220                         fprintf(stderr, "Could not restart MeshLink: %s\n", meshlink_strerror(meshlink_errno));
221                         exit(1);
222                 }
223         } else if(!strcasecmp(buf, "who")) {
224                 if(!arg) {
225                         nodes = meshlink_get_all_nodes(meshes[nodeindex], nodes, &nnodes);
226
227                         if(!nodes) {
228                                 fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
229                         } else {
230                                 printf("%zu known nodes:", nnodes);
231
232                                 for(size_t i = 0; i < nnodes; i++) {
233                                         printf(" %s", nodes[i]->name);
234                                 }
235
236                                 printf("\n");
237                         }
238                 } else {
239                         meshlink_node_t *node = meshlink_get_node(meshes[nodeindex], arg);
240
241                         if(!node) {
242                                 fprintf(stderr, "Unknown node '%s'\n", arg);
243                         } else {
244                                 printf("Node %s found, pmtu %ld\n", arg, (long int)meshlink_get_pmtu(meshes[nodeindex], node));
245                         }
246                 }
247         } else if(!strcasecmp(buf, "link")) {
248                 linkmesh();
249         } else if(!strcasecmp(buf, "eg")) {
250                 exportmeshgraph(arg);
251         } else if(!strcasecmp(buf, "egb")) {
252                 exportmeshgraph_begin(arg);
253         } else if(!strcasecmp(buf, "ege")) {
254                 exportmeshgraph_end();
255         } else if(!strcasecmp(buf, "test")) {
256                 testmesh();
257         } else if(!strcasecmp(buf, "select")) {
258                 if(!arg) {
259                         fprintf(stderr, "/select requires an argument!\n");
260                         return;
261                 }
262
263                 nodeindex = atoi(arg);
264                 printf("Index is now %d\n", nodeindex);
265         } else if(!strcasecmp(buf, "stop")) {
266                 meshlink_stop(meshes[nodeindex]);
267         } else if(!strcasecmp(buf, "quit")) {
268                 printf("Bye!\n");
269                 fclose(stdin);
270         } else if(!strcasecmp(buf, "help")) {
271                 printf(
272                         "<name>: <message>     Send a message to the given node.\n"
273                         "                      Subsequent messages don't need the <name>: prefix.\n"
274                         "/join <invitation>    Join an existing mesh using an invitation.\n"
275                         "/kick <name>          Blacklist the given node.\n"
276                         "/who [<name>]         List all nodes or show information about the given node.\n"
277                         "/link                 Link all nodes together.\n"
278                         "/eg <path>            Export graph as json file.\n"
279                         "/test                 Test functionality sending some data to all nodes\n"
280                         "/select <number>      Select the active node running the user commands\n"
281                         "/stop                 Call meshlink_stop, use /select first to select which node to stop\n"
282                         "/quit                 Exit this program.\n"
283                 );
284         } else {
285                 fprintf(stderr, "Unknown command '/%s'\n", buf);
286         }
287 }
288
289 static void parse_input(char *buf) {
290         static meshlink_node_t *destination;
291         size_t len;
292
293         if(!buf) {
294                 return;
295         }
296
297         // Remove newline.
298
299         len = strlen(buf);
300
301         if(len && buf[len - 1] == '\n') {
302                 buf[--len] = 0;
303         }
304
305         if(len && buf[len - 1] == '\r') {
306                 buf[--len] = 0;
307         }
308
309         // Ignore empty lines.
310
311         if(!len) {
312                 return;
313         }
314
315         // Commands start with '/'
316
317         if(*buf == '/') {
318                 parse_command(buf + 1);
319                 return;
320         }
321
322         // Lines in the form "name: message..." set the destination node.
323
324         char *msg = buf;
325         char *colon = strchr(buf, ':');
326
327         if(colon) {
328                 *colon = 0;
329                 msg = colon + 1;
330
331                 if(*msg == ' ') {
332                         msg++;
333                 }
334
335                 destination = meshlink_get_node(meshes[nodeindex], buf);
336
337                 if(!destination) {
338                         fprintf(stderr, "Unknown node '%s'\n", buf);
339                         return;
340                 }
341         }
342
343         if(!destination) {
344                 fprintf(stderr, "Who are you talking to? Write 'name: message...'\n");
345                 return;
346         }
347
348         if(!meshlink_send(meshes[nodeindex], destination, msg, strlen(msg) + 1)) {
349                 fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
350                 return;
351         }
352
353         printf("Message sent to '%s'.\n", destination->name);
354 }
355
356 int main(int argc, char *argv[]) {
357         const char *basebase = ".manynodes";
358         const char *graphexporttimeout = NULL;
359         char buf[1024];
360
361         if(argc > 1) {
362                 n = atoi(argv[1]);
363         }
364
365         if(n < 1) {
366                 fprintf(stderr, "Usage: %s [number of local nodes] [confbase] [prefixnodenames] [graphexport timeout]\n", argv[0]);
367                 return 1;
368         }
369
370         if(argc > 2) {
371                 basebase = argv[2];
372         }
373
374         if(argc > 3) {
375                 namesprefix = argv[3];
376         }
377
378         if(argc > 4) {
379                 graphexporttimeout = argv[4];
380         }
381
382         meshes = calloc(n, sizeof(*meshes));
383
384         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_message);
385 #ifndef _WIN32
386         mkdir(basebase, 0750);
387 #else
388         mkdir(basebase);
389 #endif
390
391         char filename[PATH_MAX];
392         char nodename[100];
393
394         for(int i = 0; i < n; i++) {
395                 snprintf(nodename, sizeof(nodename), "%snode%d", namesprefix, i);
396                 snprintf(filename, sizeof(filename), "%s/%s", basebase, nodename);
397
398                 if(n / (i + 1) > n / 4) {
399                         meshes[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_BACKBONE);
400                 } else {
401                         meshes[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_PORTABLE);
402                 }
403
404                 meshlink_set_log_cb(meshes[i], MESHLINK_DEBUG, log_message);
405
406                 if(!meshes[i]) {
407                         fprintf(stderr, "errno is: %d\n", meshlink_errno);
408                         fprintf(stderr, "Could not open %s: %s\n", filename, meshlink_strerror(meshlink_errno));
409                         return 1;
410                 }
411         }
412
413         int started = 0;
414
415         for(int i = 0; i < n; i++) {
416                 if(!meshlink_start(meshes[i])) {
417                         fprintf(stderr, "Could not start node %d: %s\n", i, meshlink_strerror(meshlink_errno));
418                 } else {
419                         started++;
420                 }
421         }
422
423         if(!started) {
424                 fprintf(stderr, "Could not start any node!\n");
425                 return 1;
426         }
427
428         if(graphexporttimeout) {
429                 exportmeshgraph_begin(graphexporttimeout);
430         }
431
432         printf("%d nodes started.\nType /help for a list of commands.\n", started);
433
434         // handle input
435         while(fgets(buf, sizeof(buf), stdin)) {
436                 parse_input(buf);
437         }
438
439         exportmeshgraph_end();
440
441         printf("Nodes stopping.\n");
442
443         for(int i = 0; i < n; i++) {
444                 meshlink_close(meshes[i]);
445         }
446
447         return 0;
448 }