From: Guus Sliepen Date: Mon, 7 Oct 2019 19:00:01 +0000 (+0200) Subject: Add an error callback. X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=4216a7e7a1897c0e34ce82e7c2c4cc82070c7b10 Add an error callback. Normally, API functions report potential errors. However, it might happen that the background thread runs into a serious error that prevents MeshLink from operating as expected. Add a callback that is called in those cases. Currently, the only time it is called is when it can not create or modify config files in the background thread. --- diff --git a/src/meshlink++.h b/src/meshlink++.h index b88746e3..e39ef81c 100644 --- a/src/meshlink++.h +++ b/src/meshlink++.h @@ -259,6 +259,12 @@ public: (void)message; } + /// This functions is called whenever MeshLink has encountered a serious error. + virtual void error(meshlink_errno_t meshlink_errno) { + /* do nothing */ + (void)meshlink_errno; + } + /// This functions is called whenever MeshLink a meta-connection attempt is made. virtual void connection_try(node *peer) { /* do nothing */ @@ -339,6 +345,7 @@ public: meshlink_set_node_pmtu_cb(handle, &node_pmtu_trampoline); meshlink_set_node_duplicate_cb(handle, &node_duplicate_trampoline); meshlink_set_log_cb(handle, MESHLINK_DEBUG, &log_trampoline); + meshlink_set_error_cb(handle, &error_trampoline); meshlink_set_channel_accept_cb(handle, &channel_accept_trampoline); meshlink_set_connection_try_cb(handle, &connection_try_trampoline); return meshlink_start(handle); @@ -952,6 +959,15 @@ private: that->log(level, message); } + static void error_trampoline(meshlink_handle_t *handle, meshlink_errno_t meshlink_errno) { + if(!(handle->priv)) { + return; + } + + meshlink::mesh *that = static_cast(handle->priv); + that->error(meshlink_errno); + } + static void connection_try_trampoline(meshlink_handle_t *handle, meshlink_node_t *peer) { if(!(handle->priv)) { return; diff --git a/src/meshlink.c b/src/meshlink.c index 227df29f..4162b36e 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -1718,6 +1718,17 @@ void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, me } } +void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb) { + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; + return; + } + + pthread_mutex_lock(&(mesh->mesh_mutex)); + mesh->error_cb = cb; + pthread_mutex_unlock(&(mesh->mesh_mutex)); +} + bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { meshlink_packethdr_t *hdr; @@ -3524,6 +3535,22 @@ void handle_network_change(meshlink_handle_t *mesh, bool online) { retry(mesh); } +void call_error_cb(meshlink_handle_t *mesh) { + // We should only call the callback function if we are in the background thread. + if(!mesh->error_cb) { + return; + } + + if(!mesh->threadstarted) { + return; + } + + if(mesh->thread == pthread_self()) { + mesh->error_cb(mesh, meshlink_errno); + } +} + + static void __attribute__((constructor)) meshlink_init(void) { crypto_init(); } diff --git a/src/meshlink.h b/src/meshlink.h index da5cc131..00aa19b2 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -511,6 +511,38 @@ typedef void (*meshlink_log_cb_t)(struct meshlink_handle *mesh, meshlink_log_lev */ extern void meshlink_set_log_cb(struct meshlink_handle *mesh, meshlink_log_level_t level, meshlink_log_cb_t cb); +/// A callback for receiving error conditions encountered by the MeshLink thread. +/** @param mesh A handle which represents an instance of MeshLink, or NULL. + * @param errno The error code describing what kind of error occured. + */ +typedef void (*meshlink_error_cb_t)(struct meshlink_handle *mesh, meshlink_errno_t meshlink_errno); + +/// Set the error callback. +/** This functions sets the callback that is called whenever the MeshLink thread encounters a serious error. + * + * While most API functions report an error directly to the caller in case something went wrong, + * MeshLink also runs a background thread which can encounter error conditions. + * Most of them will be dealt with automatically, however there can be errors that will prevent MeshLink from + * working correctly. When the callback is called, it means that MeshLink is no longer functioning + * as expected. The application should then present an error message and shut down, or perform any other + * action it deems appropriate. + * + * The callback is run in MeshLink's own thread. + * It is important that the callback uses apprioriate methods (queues, pipes, locking, etc.) + * to hand the data over to the application's thread. + * The callback should also not block itself and return as quickly as possible. + * + * Even though the callback signals a serious error inside MeshLink, all open handles are still valid, + * and the application should close handles in exactly the same it would have to do if the callback + * was not called. This must not be done inside the callback itself. + * + * \memberof meshlink_handle + * @param mesh A handle which represents an instance of MeshLink, or NULL. + * @param cb A pointer to the function which will be called when a serious error is encountered. + * If a NULL pointer is given, the callback will be disabled. + */ +extern void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb); + /// Send data to another node. /** This functions sends one packet of data to another node in the mesh. * The packet is sent using UDP semantics, which means that diff --git a/src/meshlink.sym b/src/meshlink.sym index 207da7c4..d94947c0 100644 --- a/src/meshlink.sym +++ b/src/meshlink.sym @@ -63,6 +63,7 @@ meshlink_set_channel_sndbuf meshlink_set_connection_try_cb meshlink_set_default_blacklist meshlink_set_dev_class_timeouts +meshlink_set_error_cb meshlink_set_invitation_timeout meshlink_set_log_cb meshlink_set_node_duplicate_cb diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index cbb98c6f..ba7f7d15 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -143,6 +143,7 @@ struct meshlink_handle { meshlink_channel_accept_cb_t channel_accept_cb; meshlink_node_duplicate_cb_t node_duplicate_cb; meshlink_connection_try_cb_t connection_try_cb; + meshlink_error_cb_t error_cb; // Mesh parameters char *appname; @@ -257,6 +258,7 @@ extern meshlink_log_level_t global_log_level; extern meshlink_log_cb_t global_log_cb; extern void handle_duplicate_node(meshlink_handle_t *mesh, struct node_t *n); extern void handle_network_change(meshlink_handle_t *mesh, bool online); +extern void call_error_cb(meshlink_handle_t *mesh); /// Per-instance PRNG static inline int prng(meshlink_handle_t *mesh, uint64_t max) { diff --git a/src/net_setup.c b/src/net_setup.c index 367ccd29..b4a16c7e 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -270,7 +270,13 @@ bool node_write_config(meshlink_handle_t *mesh, node_t *n) { } config_t config = {buf, packmsg_output_size(&out, buf)}; - return config_write(mesh, "current", n->name, &config, mesh->config_key); + + if(!config_write(mesh, "current", n->name, &config, mesh->config_key)) { + call_error_cb(mesh); + return false; + } + + return true; } static bool load_node(meshlink_handle_t *mesh, const char *name, void *priv) {