pthread_cond_wait(&mesh->cond, &mesh->mutex);
mesh->threadstarted = true;
+ mesh->self->last_reachable = time(NULL);
+ mesh->self->status.dirty = true;
pthread_mutex_unlock(&mesh->mutex);
return true;
pthread_mutex_lock(&mesh->mutex);
logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
+ if(mesh->self) {
+ mesh->self->last_unreachable = time(NULL);
+ mesh->self->status.dirty = true;
+ }
+
// Shut down the main thread
event_loop_stop(&mesh->loop);
return false;
}
+struct time_range {
+ time_t start;
+ time_t end;
+};
+
+static bool search_node_by_last_reachable(const node_t *node, const void *condition) {
+ const struct time_range *range = condition;
+ time_t start = node->last_reachable;
+ time_t end = node->last_unreachable;
+
+ if(end < start) {
+ end = time(NULL);
+
+ if(end < start) {
+ start = end;
+ }
+ }
+
+ if(range->end >= range->start) {
+ return start <= range->end && end >= range->start;
+ } else {
+ return start > range->start || end < range->end;
+ }
+}
+
meshlink_node_t **meshlink_get_all_nodes_by_dev_class(meshlink_handle_t *mesh, dev_class_t devclass, meshlink_node_t **nodes, size_t *nmemb) {
if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT || !nmemb) {
meshlink_errno = MESHLINK_EINVAL;
return meshlink_get_all_nodes_by_condition(mesh, submesh, nodes, nmemb, search_node_by_submesh);
}
+meshlink_node_t **meshlink_get_all_nodes_by_last_reachable(meshlink_handle_t *mesh, time_t start, time_t end, meshlink_node_t **nodes, size_t *nmemb) {
+ if(!mesh || !nmemb) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ struct time_range range = {start, end};
+
+ return meshlink_get_all_nodes_by_condition(mesh, &range, nodes, nmemb, search_node_by_last_reachable);
+}
+
dev_class_t meshlink_get_node_dev_class(meshlink_handle_t *mesh, meshlink_node_t *node) {
if(!mesh || !node) {
meshlink_errno = MESHLINK_EINVAL;
packmsg_add_sockaddr(&out, &mesh->self->recent[i]);
}
+ packmsg_add_int64(&out, 0);
+ packmsg_add_int64(&out, 0);
+
pthread_mutex_unlock(&mesh->mutex);
if(!packmsg_output_ok(&out)) {
*/
extern struct meshlink_node **meshlink_get_all_nodes_by_submesh(struct meshlink_handle *mesh, struct meshlink_submesh *submesh, struct meshlink_node **nodes, size_t *nmemb) __attribute__((__warn_unused_result__));
+/// Get the list of all nodes by time they were last reachable.
+/** This function returns a list with handles for all the nodes whose last known reachability time overlaps with the given time range.
+ * If the range includes 0, it will count nodes that were never online.
+ * If start is bigger than end, the result will be inverted.
+ *
+ * \memberof meshlink_handle
+ * @param mesh A handle which represents an instance of MeshLink.
+ * @param start Start time.
+ * @param end End time.
+ * @param nodes A pointer to a previously allocated array of pointers to struct meshlink_node, or NULL in which case MeshLink will allocate a new array.
+ * The application can supply an array it allocated itself with malloc, or the return value from the previous call to this function (which is the preferred way).
+ * The application is allowed to call free() on the array whenever it wishes.
+ * The pointers in the array are valid until meshlink_close() is called.
+ * @param nmemb A pointer to a variable holding the number of nodes that were reachable within the period given by @a start and @a end.
+ * In case the @a nodes argument is not NULL, MeshLink might call realloc() on the array to change its size.
+ * The contents of this variable will be changed to reflect the new size of the array.
+ *
+ * @return A pointer to an array containing pointers to all known nodes that were reachable within the period given by @a start and @a end.
+ * If the @a nodes argument was not NULL, then the return value can either be the same value or a different value.
+ * If it is a new value, the old value of @a nodes should not be used anymore.
+ * If the new value is NULL, then the old array will have been freed by MeshLink.
+ */
+extern struct meshlink_node **meshlink_get_all_nodes_by_last_reachable(struct meshlink_handle *mesh, time_t start, time_t end, struct meshlink_node **nodes, size_t *nmemb) __attribute__((__warn_unused_result__));
+
/// Get the node's device class.
/** This function returns the device class of the given node.
*
--- /dev/null
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <assert.h>
+
+#include "meshlink.h"
+#include "utils.h"
+
+struct sync_flag bar_reachable;
+
+void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
+ (void)mesh;
+
+ if(reachable && !strcmp(node->name, "bar")) {
+ set_sync_flag(&bar_reachable, true);
+ }
+}
+
+int main() {
+ struct meshlink_node **nodes = NULL;
+ size_t nnodes = 0;
+
+ meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
+
+ // Open new meshlink instances.
+
+ assert(meshlink_destroy("get_all_nodes_conf.1"));
+ assert(meshlink_destroy("get_all_nodes_conf.2"));
+ assert(meshlink_destroy("get_all_nodes_conf.3"));
+
+ meshlink_handle_t *mesh[3];
+ mesh[0] = meshlink_open("get_all_nodes_conf.1", "foo", "import-export", DEV_CLASS_BACKBONE);
+ assert(mesh[0]);
+
+ mesh[1] = meshlink_open("get_all_nodes_conf.2", "bar", "import-export", DEV_CLASS_STATIONARY);
+ assert(mesh[1]);
+
+ mesh[2] = meshlink_open("get_all_nodes_conf.3", "baz", "get-all-nodes", DEV_CLASS_STATIONARY);
+ assert(mesh[2]);
+
+ // Check that we only know about ourself.
+
+ nodes = meshlink_get_all_nodes(mesh[0], nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ // We should never have been online.
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
+ assert(nnodes == 0);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ // Let nodes know about each other.
+
+ for(int i = 0; i < 3; i++) {
+ meshlink_enable_discovery(mesh[i], false);
+ assert(meshlink_add_address(mesh[i], "localhost"));
+ char *data = meshlink_export(mesh[i]);
+ assert(data);
+
+ for(int j = 0; j < 3; j++) {
+ if(i == j) {
+ continue;
+ }
+
+ assert(meshlink_import(mesh[j], data));
+ }
+ }
+
+ // We should know about all nodes now, and their device class.
+
+ nodes = meshlink_get_all_nodes(mesh[0], nodes, &nnodes);
+ assert(nnodes == 3);
+
+ nodes = meshlink_get_all_nodes_by_dev_class(mesh[0], DEV_CLASS_BACKBONE, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_dev_class(mesh[0], DEV_CLASS_STATIONARY, nodes, &nnodes);
+ assert(nnodes == 2);
+
+ // But no node should have been online.
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
+ assert(nnodes == 0);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
+ assert(nnodes == 3);
+
+ // Start foo.
+
+ time_t foo_started = time(NULL);
+ assert(meshlink_start(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
+ assert(nnodes == 2);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], foo_started - 1, -1, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started - 1, nodes, &nnodes);
+ assert(nnodes == 0);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started + 1, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ // Start bar and wait for it to connect.
+
+ meshlink_set_node_status_cb(mesh[0], status_cb);
+
+ sleep(2);
+ assert(meshlink_start(mesh[1]));
+ assert(wait_sync_flag(&bar_reachable, 20));
+ time_t bar_started = time(NULL);
+
+ // Validate time ranges.
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
+ assert(nnodes == 2);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_node(mesh[0], "baz"));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started + 1, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_started, bar_started, nodes, &nnodes);
+ assert(nnodes == 2);
+ assert(nodes[0] == meshlink_get_node(mesh[0], "bar"));
+ assert(nodes[1] == meshlink_get_self(mesh[0]));
+
+ // Stop bar.
+
+ meshlink_stop(mesh[1]);
+ sleep(2);
+ time_t bar_stopped = time(NULL);
+
+ // Validate we can see when bar was reachable.
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_stopped, bar_stopped, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_started, bar_started, nodes, &nnodes);
+ assert(nnodes == 2);
+ assert(nodes[0] == meshlink_get_node(mesh[0], "bar"));
+ assert(nodes[1] == meshlink_get_self(mesh[0]));
+
+ // Close and restart foo, check that it remembers correctly.
+
+ meshlink_close(mesh[0]);
+ sleep(2);
+ time_t foo_stopped = time(NULL);
+ mesh[0] = meshlink_open("get_all_nodes_conf.1", "foo", "import-export", DEV_CLASS_BACKBONE);
+ assert(mesh[0]);
+
+ nodes = meshlink_get_all_nodes(mesh[0], nodes, &nnodes);
+ assert(nnodes == 3);
+
+ nodes = meshlink_get_all_nodes_by_dev_class(mesh[0], DEV_CLASS_BACKBONE, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_dev_class(mesh[0], DEV_CLASS_STATIONARY, nodes, &nnodes);
+ assert(nnodes == 2);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_node(mesh[0], "baz"));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
+ assert(nnodes == 2);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started - 1, nodes, &nnodes);
+ assert(nnodes == 0);
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started + 1, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_started, bar_started, nodes, &nnodes);
+ assert(nnodes == 2);
+ assert(nodes[0] == meshlink_get_node(mesh[0], "bar"));
+ assert(nodes[1] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_stopped, bar_stopped, nodes, &nnodes);
+ assert(nnodes == 1);
+ assert(nodes[0] == meshlink_get_self(mesh[0]));
+
+ nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], foo_stopped, -1, nodes, &nnodes);
+ assert(nnodes == 0);
+
+ // Clean up.
+
+ for(int i = 0; i < 3; i++) {
+ meshlink_close(mesh[i]);
+ }
+}