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