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