From fc6b5da57b5c7a30b5cdb227fd6b98098a665159 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Tue, 29 Jul 2014 18:55:25 +0200 Subject: [PATCH] Start of the manynodes example. This example program starts many instances of MeshLink in a single program, and can let them connect to each other. This is mainly usable for testing scalability. --- examples/Makefile.am | 5 +- examples/manynodes.c | 260 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 examples/manynodes.c diff --git a/examples/Makefile.am b/examples/Makefile.am index af8f0f45..9be95b13 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,4 +1,4 @@ -bin_PROGRAMS = meshlinkapp chat chatpp +bin_PROGRAMS = meshlinkapp chat chatpp manynodes AM_CPPFLAGS = -I../src @@ -10,3 +10,6 @@ chat_LDADD = ../src/libmeshlink.la chatpp_SOURCES = chatpp.cc chatpp_LDADD = ../src/libmeshlink.la + +manynodes_SOURCES = manynodes.cc +manynodes_LDADD = ../src/libmeshlink.la diff --git a/examples/manynodes.c b/examples/manynodes.c new file mode 100644 index 00000000..86883ea1 --- /dev/null +++ b/examples/manynodes.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../src/meshlink.h" + +static int n = 100; +static meshlink_handle_t **mesh; + +static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) { + const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}; + fprintf(stderr, "%s: %s\n", levelstr[level], text); +} + +static void receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) { + const char *msg = data; + + if(!len || msg[len - 1]) { + fprintf(stderr, "Received invalid data from %s\n", source->name); + return; + } + + printf("%s says: %s\n", source->name, msg); +} + +static void node_status(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) { + if(reachable) + printf("%s joined.\n", node->name); + else + printf("%s left.\n", node->name); +} + +// Make all nodes know about each other by importing each others public keys and addresses. +static void linkmesh() { + for(int i = 0; i < n; i++) { + char *datai = meshlink_export(mesh[i]); + + for(int j = i + 1; j < n; j++) { + char *dataj = meshlink_export(mesh[j]); + meshlink_import(mesh[i], dataj); + meshlink_import(mesh[j], datai); + free(dataj); + } + + free(datai); + } +} + +static void parse_command(char *buf) { + char *arg = strchr(buf, ' '); + if(arg) + *arg++ = 0; + + if(!strcasecmp(buf, "invite")) { + char *invitation; + + if(!arg) { + fprintf(stderr, "/invite requires an argument!\n"); + return; + } + + invitation = meshlink_invite(mesh[0], arg); + if(!invitation) { + fprintf(stderr, "Could not invite '%s': %s\n", arg, meshlink_strerror(meshlink_errno)); + return; + } + + printf("Invitation for %s: %s\n", arg, invitation); + free(invitation); + } else if(!strcasecmp(buf, "join")) { + if(!arg) { + fprintf(stderr, "/join requires an argument!\n"); + return; + } + + if(!meshlink_join(mesh[0], arg)) + fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno)); + else + fprintf(stderr, "Invitation accepted!\n"); + } else if(!strcasecmp(buf, "kick")) { + if(!arg) { + fprintf(stderr, "/kick requires an argument!\n"); + return; + } + + meshlink_node_t *node = meshlink_get_node(mesh[0], arg); + if(!node) { + fprintf(stderr, "Unknown node '%s'\n", arg); + return; + } + + meshlink_blacklist(mesh[0], node); + + printf("Node '%s' blacklisted.\n", arg); + } else if(!strcasecmp(buf, "who")) { + if(!arg) { + meshlink_node_t *nodes[100]; + size_t n = meshlink_get_all_nodes(mesh[0], nodes, 100); + if(!n) { + fprintf(stderr, "No nodes known!\n"); + } else { + printf("Known nodes:"); + for(int i = 0; i < n && i < 100; i++) + printf(" %s", nodes[i]->name); + if(n > 100) + printf(" (and %zu more)", n - 100); + printf("\n"); + } + } else { + meshlink_node_t *node = meshlink_get_node(mesh[0], arg); + if(!node) { + fprintf(stderr, "Unknown node '%s'\n", arg); + } else { + printf("Node %s found\n", arg); + } + } + } else if(!strcasecmp(buf, "link")) { + linkmesh(); + } else if(!strcasecmp(buf, "quit")) { + printf("Bye!\n"); + fclose(stdin); + } else if(!strcasecmp(buf, "help")) { + printf( + ": Send a message to the given node.\n" + " Subsequent messages don't need the : prefix.\n" + "/invite Create an invitation for a new node.\n" + "/join Join an existing mesh using an invitation.\n" + "/kick Blacklist the given node.\n" + "/who [] List all nodes or show information about the given node.\n" + "/link Link all nodes together.\n" + "/quit Exit this program.\n" + ); + } else { + fprintf(stderr, "Unknown command '/%s'\n", buf); + } +} + +static void parse_input(char *buf) { + static meshlink_node_t *destination; + size_t len; + + if(!buf) + return; + + // Remove newline. + + len = strlen(buf); + + if(len && buf[len - 1] == '\n') + buf[--len] = 0; + + if(len && buf[len - 1] == '\r') + buf[--len] = 0; + + // Ignore empty lines. + + if(!len) + return; + + // Commands start with '/' + + if(*buf == '/') + return parse_command(buf + 1); + + // Lines in the form "name: message..." set the destination node. + + char *msg = buf; + char *colon = strchr(buf, ':'); + + if(colon) { + *colon = 0; + msg = colon + 1; + if(*msg == ' ') + msg++; + + destination = meshlink_get_node(mesh[0], buf); + if(!destination) { + fprintf(stderr, "Unknown node '%s'\n", buf); + return; + } + } + + if(!destination) { + fprintf(stderr, "Who are you talking to? Write 'name: message...'\n"); + return; + } + + if(!meshlink_send(mesh[0], destination, msg, strlen(msg) + 1)) { + fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno)); + return; + } + + printf("Message sent to '%s'.\n", destination->name); +} + +int main(int argc, char *argv[]) { + const char *basebase = ".manynodes"; + char buf[1024]; + + if(argc > 1) + n = atoi(argv[1]); + + if(n < 1) { + fprintf(stderr, "Usage: %s [number of local nodes] [confbase]\n", argv[0]); + return 1; + } + + if(argc > 2) + basebase = argv[2]; + + mesh = calloc(n, sizeof *mesh); + + mkdir(basebase, 0750); + + char filename[PATH_MAX]; + char nodename[100]; + for(int i = 0; i < n; i++) { + snprintf(nodename, sizeof nodename, "node%d", i); + snprintf(filename, sizeof filename, "%s/%s", basebase, nodename); + bool itsnew = access(filename, R_OK); + mesh[i] = meshlink_open(filename, nodename); + if(itsnew) + meshlink_add_address(mesh[i], "localhost"); + if(!mesh[i]) { + fprintf(stderr, "errno is: %d\n", meshlink_errno); + fprintf(stderr, "Could not open %s: %s\n", filename, meshlink_strerror(meshlink_errno)); + return 1; + } + } + + int started = 0; + + for(int i = 0; i < n; i++) { + if(!meshlink_start(mesh[i])) + fprintf(stderr, "Could not start node %d: %s\n", i, meshlink_strerror(meshlink_errno)); + else + started++; + } + + if(!started) { + fprintf(stderr, "Could not start any node!\n"); + return 1; + } + + printf("%d nodes started.\nType /help for a list of commands.\n", started); + + while(fgets(buf, sizeof buf, stdin)) + parse_input(buf); + + printf("Nodes stopping.\n"); + + for(int i = 0; i < n; i++) + meshlink_close(mesh[i]); + + return 0; +} -- 2.39.2