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