]> git.meshlink.io Git - meshlink/blob - examples/manynodes.c
Automatically configure and compile catta.
[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                 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                         for(int i = 0; i < nnodes; i++) {
55                                 //printf(" %s\n", nodes[i]->name);
56                                 if(!meshlink_send(mesh[nindex], nodes[i], "magic", strlen("magic") + 1))
57                                         fprintf(stderr, "Could not send message to '%s': %s\n", nodes[i]->name, meshlink_strerror(meshlink_errno));
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         assert(path);
82
83         struct stat ps;
84         int psr = stat(path, &ps);
85
86         if(psr == 0 || errno != ENOENT) {
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                 perror("stream");
99                 return false;
100         }
101
102         if(!devtool_export_json_all_edges_state(mesh[0], stream)) {
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         struct timeval ts;
115         gettimeofday(&ts, NULL);
116
117         char name[1024];
118         snprintf(name, sizeof(name), "%sgraph_%ld_%03ld.json", namesprefix, ts.tv_sec, ts.tv_usec / 1000);
119
120         exportmeshgraph(name);
121 }
122
123 #ifndef _WIN32
124 static bool exportmeshgraph_started = false;
125
126 static bool exportmeshgraph_end(const char *none) {
127         if(!exportmeshgraph_started)
128                 return false;
129
130         struct itimerval zero_timer = { 0 };
131         setitimer(ITIMER_REAL, &zero_timer, NULL);
132
133         exportmeshgraph_started = false;
134
135         return true;
136 }
137
138 static bool exportmeshgraph_begin(const char *timeout_str) {
139         if(!timeout_str)
140                 return false;
141
142         if(exportmeshgraph_started) {
143                 if(!exportmeshgraph_end(NULL))
144                         return false;
145         }
146
147         // get timeout
148         int timeout = atoi(timeout_str);
149
150         if(timeout < 100)
151                 timeout = 100;
152
153         int timeout_sec = timeout / 1000;
154         int timeout_msec = timeout % 1000;
155
156         /* Install timer_handler as the signal handler for SIGALRM. */
157         signal(SIGALRM, exportmeshgraph_timer);
158
159         /* Configure the timer to expire immediately... */
160         struct itimerval timer;
161         timer.it_value.tv_sec = 0;
162         timer.it_value.tv_usec = 1000;
163
164         /* ... and every X msec after that. */
165         timer.it_interval.tv_sec = timeout_sec;
166         timer.it_interval.tv_usec = timeout_msec * 1000;
167
168         /* Start a real timer. */
169         setitimer(ITIMER_REAL, &timer, NULL);
170
171         exportmeshgraph_started = true;
172
173         return true;
174 }
175 #else
176 static bool exportmeshgraph_end(const char *none) {
177         return false;
178 }
179
180 static bool exportmeshgraph_begin(const char *timeout_str) {
181         return false;
182 }
183 #endif
184
185 static void parse_command(char *buf) {
186         char *arg = strchr(buf, ' ');
187         if(arg)
188                 *arg++ = 0;
189
190         if(!strcasecmp(buf, "invite")) {
191                 char *invitation;
192
193                 if(!arg) {
194                         fprintf(stderr, "/invite requires an argument!\n");
195                         return;
196                 }
197
198                 invitation = meshlink_invite(mesh[nodeindex], arg);
199                 if(!invitation) {
200                         fprintf(stderr, "Could not invite '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
201                         return;
202                 }
203
204                 printf("Invitation for %s: %s\n", arg, invitation);
205                 free(invitation);
206         } else if(!strcasecmp(buf, "join")) {
207                 if(!arg) {
208                         fprintf(stderr, "/join requires an argument!\n");
209                         return;
210                 }
211                 meshlink_stop(mesh[nodeindex]);
212                 if(!meshlink_join(mesh[nodeindex], arg))
213                         fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
214                 else
215                         fprintf(stderr, "Invitation accepted!\n");
216                 if(!meshlink_start(mesh[nodeindex])) {
217                         fprintf(stderr, "Could not restart MeshLink: %s\n", meshlink_strerror(meshlink_errno));
218                         exit(1);
219                 }
220         } else if(!strcasecmp(buf, "kick")) {
221                 if(!arg) {
222                         fprintf(stderr, "/kick requires an argument!\n");
223                         return;
224                 }
225
226                 meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
227                 if(!node) {
228                         fprintf(stderr, "Unknown node '%s'\n", arg);
229                         return;
230                 }
231
232                 meshlink_blacklist(mesh[nodeindex], node);
233
234                 printf("Node '%s' blacklisted.\n", arg);
235         } else if(!strcasecmp(buf, "who")) {
236                 if(!arg) {
237                         nodes = meshlink_get_all_nodes(mesh[nodeindex], nodes, &nnodes);
238                         if(!nodes)
239                                 fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
240                         else {
241                                 printf("%zu known nodes:", nnodes);
242                                 for(int i = 0; i < nnodes; i++)
243                                         printf(" %s", nodes[i]->name);
244                                 printf("\n");
245                         }
246                 } else {
247                         meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
248                         if(!node)
249                                 fprintf(stderr, "Unknown node '%s'\n", arg);
250                         else
251                                 printf("Node %s found, pmtu %zd\n", arg, meshlink_get_pmtu(mesh[nodeindex], node));
252                 }
253         } else if(!strcasecmp(buf, "link"))
254                 linkmesh();
255         else if(!strcasecmp(buf, "eg"))
256                 exportmeshgraph(arg);
257         else if(!strcasecmp(buf, "egb"))
258                 exportmeshgraph_begin(arg);
259         else if(!strcasecmp(buf, "ege"))
260                 exportmeshgraph_end(NULL);
261         else if(!strcasecmp(buf, "test"))
262                 testmesh();
263         else if(!strcasecmp(buf, "select")) {
264                 if(!arg) {
265                         fprintf(stderr, "/select requires an argument!\n");
266                         return;
267                 }
268                 nodeindex = atoi(arg);
269                 printf("Index is now %d\n", nodeindex);
270         } else if(!strcasecmp(buf, "stop"))
271                 meshlink_stop(mesh[nodeindex]);
272         else if(!strcasecmp(buf, "quit")) {
273                 printf("Bye!\n");
274                 fclose(stdin);
275         } else if(!strcasecmp(buf, "help")) {
276                 printf(
277                         "<name>: <message>     Send a message to the given node.\n"
278                         "                      Subsequent messages don't need the <name>: prefix.\n"
279                         "/invite <name>        Create an invitation for a new node.\n"
280                         "/join <invitation>    Join an existing mesh using an invitation.\n"
281                         "/kick <name>          Blacklist the given node.\n"
282                         "/who [<name>]         List all nodes or show information about the given node.\n"
283                         "/link                 Link all nodes together.\n"
284                         "/eg <path>            Export graph as json file.\n"
285                         "/test                 Test functionality sending some data to all nodes\n"
286                         "/select <number>      Select the active node running the user commands\n"
287                         "/stop                 Call meshlink_stop, use /select first to select which node to stop\n"
288                         "/quit                 Exit this program.\n"
289                 );
290         } else
291                 fprintf(stderr, "Unknown command '/%s'\n", buf);
292 }
293
294 static void parse_input(char *buf) {
295         static meshlink_node_t *destination;
296         size_t len;
297
298         if(!buf)
299                 return;
300
301         // Remove newline.
302
303         len = strlen(buf);
304
305         if(len && buf[len - 1] == '\n')
306                 buf[--len] = 0;
307
308         if(len && buf[len - 1] == '\r')
309                 buf[--len] = 0;
310
311         // Ignore empty lines.
312
313         if(!len)
314                 return;
315
316         // Commands start with '/'
317
318         if(*buf == '/')
319                 return parse_command(buf + 1);
320
321         // Lines in the form "name: message..." set the destination node.
322
323         char *msg = buf;
324         char *colon = strchr(buf, ':');
325
326         if(colon) {
327                 *colon = 0;
328                 msg = colon + 1;
329                 if(*msg == ' ')
330                         msg++;
331
332                 destination = meshlink_get_node(mesh[nodeindex], buf);
333                 if(!destination) {
334                         fprintf(stderr, "Unknown node '%s'\n", buf);
335                         return;
336                 }
337         }
338
339         if(!destination) {
340                 fprintf(stderr, "Who are you talking to? Write 'name: message...'\n");
341                 return;
342         }
343
344         if(!meshlink_send(mesh[nodeindex], destination, msg, strlen(msg) + 1)) {
345                 fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
346                 return;
347         }
348
349         printf("Message sent to '%s'.\n", destination->name);
350 }
351
352 int main(int argc, char *argv[]) {
353         const char *basebase = ".manynodes";
354         const char *graphexporttimeout = NULL;
355         char buf[1024];
356
357         if(argc > 1)
358                 n = atoi(argv[1]);
359
360         if(n < 1) {
361                 fprintf(stderr, "Usage: %s [number of local nodes] [confbase] [prefixnodenames] [graphexport timeout]\n", argv[0]);
362                 return 1;
363         }
364
365         if(argc > 2)
366                 basebase = argv[2];
367
368         if(argc > 3)
369                 namesprefix = argv[3];
370
371         if(argc > 4)
372                 graphexporttimeout = argv[4];
373
374         mesh = calloc(n, sizeof(*mesh));
375
376         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_message);
377 #ifndef _WIN32
378         mkdir(basebase, 0750);
379 #else
380         mkdir(basebase);
381 #endif
382
383         char filename[PATH_MAX];
384         char nodename[100];
385         for(int i = 0; i < n; i++) {
386                 snprintf(nodename, sizeof(nodename), "%snode%d", namesprefix, i);
387                 snprintf(filename, sizeof(filename), "%s/%s", basebase, nodename);
388                 if(n / (i + 1) > n / 4)
389                         mesh[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_BACKBONE);
390                 else
391                         mesh[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_PORTABLE);
392                 meshlink_set_log_cb(mesh[i], MESHLINK_DEBUG, log_message);
393                 if(!mesh[i]) {
394                         fprintf(stderr, "errno is: %d\n", meshlink_errno);
395                         fprintf(stderr, "Could not open %s: %s\n", filename, meshlink_strerror(meshlink_errno));
396                         return 1;
397                 }
398         }
399
400         int started = 0;
401
402         for(int i = 0; i < n; i++) {
403                 if(!meshlink_start(mesh[i]))
404                         fprintf(stderr, "Could not start node %d: %s\n", i, meshlink_strerror(meshlink_errno));
405                 else
406                         started++;
407         }
408
409         if(!started) {
410                 fprintf(stderr, "Could not start any node!\n");
411                 return 1;
412         }
413
414         if(graphexporttimeout)
415                 exportmeshgraph_begin(graphexporttimeout);
416
417         printf("%d nodes started.\nType /help for a list of commands.\n", started);
418
419         // handle input
420         while(fgets(buf, sizeof(buf), stdin))
421                 parse_input(buf);
422
423         exportmeshgraph_end(NULL);
424
425         printf("Nodes stopping.\n");
426
427         for(int i = 0; i < n; i++)
428                 meshlink_close(mesh[i]);
429
430         return 0;
431 }