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