From 6346e3f4a100f958d5aaeffd867b10ed7cd0cd92 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 15 Feb 2021 23:48:09 +0100 Subject: [PATCH] Allow a different location for the lock file. In order to reduce the write load on the configuration directory as much as possible, allow the location of the lock file to be set by the application to somewhere outside the configuration directory. On Linux, it can be put in /dev/shm. --- src/conf.c | 13 +++-- src/conf.h | 2 +- src/meshlink.c | 112 ++++++++++++++++++++++++++-------------- src/meshlink.h | 26 ++++++++++ src/meshlink.sym | 2 + src/meshlink_internal.h | 1 + 6 files changed, 109 insertions(+), 47 deletions(-) diff --git a/src/conf.c b/src/conf.c index af080384..db91d5fb 100644 --- a/src/conf.c +++ b/src/conf.c @@ -484,11 +484,13 @@ bool meshlink_confbase_exists(meshlink_handle_t *mesh) { } /// Lock the main configuration file. Creates confbase if necessary. -bool main_config_lock(meshlink_handle_t *mesh) { +bool main_config_lock(meshlink_handle_t *mesh, const char *lock_filename) { if(!mesh->confbase) { return true; } + assert(lock_filename); + if(mkdir(mesh->confbase, 0700) && errno != EEXIST) { logger(NULL, MESHLINK_ERROR, "Cannot create configuration directory %s: %s", mesh->confbase, strerror(errno)); meshlink_close(mesh); @@ -496,13 +498,10 @@ bool main_config_lock(meshlink_handle_t *mesh) { return NULL; } - char path[PATH_MAX]; - snprintf(path, sizeof(path), "%s" SLASH "meshlink.lock", mesh->confbase); - - mesh->lockfile = fopen(path, "w+"); + mesh->lockfile = fopen(lock_filename, "w+"); if(!mesh->lockfile) { - logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", path, strerror(errno)); + logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", lock_filename, strerror(errno)); meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -516,7 +515,7 @@ bool main_config_lock(meshlink_handle_t *mesh) { #else if(flock(fileno(mesh->lockfile), LOCK_EX | LOCK_NB) != 0) { - logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", path, strerror(errno)); + logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", lock_filename, strerror(errno)); fclose(mesh->lockfile); mesh->lockfile = NULL; meshlink_errno = MESHLINK_EBUSY; diff --git a/src/conf.h b/src/conf.h index b42a229b..656a0acc 100644 --- a/src/conf.h +++ b/src/conf.h @@ -43,7 +43,7 @@ bool config_sync(struct meshlink_handle *mesh, const char *conf_subdir) __attrib bool sync_path(const char *path) __attribute__((__warn_unused_result__)); bool main_config_exists(struct meshlink_handle *mesh, const char *conf_subdir) __attribute__((__warn_unused_result__)); -bool main_config_lock(struct meshlink_handle *mesh) __attribute__((__warn_unused_result__)); +bool main_config_lock(struct meshlink_handle *mesh, const char *lock_filename) __attribute__((__warn_unused_result__)); void main_config_unlock(struct meshlink_handle *mesh); bool main_config_read(struct meshlink_handle *mesh, const char *conf_subdir, struct config_t *, void *key) __attribute__((__warn_unused_result__)); bool main_config_write(struct meshlink_handle *mesh, const char *conf_subdir, const struct config_t *, void *key) __attribute__((__warn_unused_result__)); diff --git a/src/meshlink.c b/src/meshlink.c index 8b9e195f..aa29ce89 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -1230,6 +1230,8 @@ meshlink_open_params_t *meshlink_open_params_init(const char *confbase, const ch params->devclass = devclass; params->netns = -1; + xasprintf(¶ms->lock_filename, "%s" SLASH "meshlink.lock", confbase); + return params; } @@ -1273,6 +1275,18 @@ bool meshlink_open_params_set_storage_policy(meshlink_open_params_t *params, mes return true; } +bool meshlink_open_params_set_lock_filename(meshlink_open_params_t *params, const char *filename) { + if(!params || !filename) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + free(params->lock_filename); + params->lock_filename = xstrdup(filename); + + return true; +} + bool meshlink_encrypted_key_rotate(meshlink_handle_t *mesh, const void *new_key, size_t new_keylen) { if(!mesh || !new_key || !new_keylen) { logger(mesh, MESHLINK_ERROR, "Invalid arguments given!\n"); @@ -1354,6 +1368,7 @@ void meshlink_open_params_free(meshlink_open_params_t *params) { free(params->confbase); free(params->name); free(params->appname); + free(params->lock_filename); free(params); } @@ -1373,15 +1388,18 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c return NULL; } - /* Create a temporary struct on the stack, to avoid allocating and freeing one. */ - meshlink_open_params_t params; - memset(¶ms, 0, sizeof(params)); + char lock_filename[PATH_MAX]; + snprintf(lock_filename, sizeof(lock_filename), "%s" SLASH "meshlink.lock", confbase); - params.confbase = (char *)confbase; - params.name = (char *)name; - params.appname = (char *)appname; - params.devclass = devclass; - params.netns = -1; + /* Create a temporary struct on the stack, to avoid allocating and freeing one. */ + meshlink_open_params_t params = { + .confbase = (char *)confbase, + .lock_filename = lock_filename, + .name = (char *)name, + .appname = (char *)appname, + .devclass = devclass, + .netns = -1, + }; return meshlink_open_ex(¶ms); } @@ -1393,15 +1411,18 @@ meshlink_handle_t *meshlink_open_encrypted(const char *confbase, const char *nam return NULL; } - /* Create a temporary struct on the stack, to avoid allocating and freeing one. */ - meshlink_open_params_t params; - memset(¶ms, 0, sizeof(params)); + char lock_filename[PATH_MAX]; + snprintf(lock_filename, sizeof(lock_filename), "%s" SLASH "meshlink.lock", confbase); - params.confbase = (char *)confbase; - params.name = (char *)name; - params.appname = (char *)appname; - params.devclass = devclass; - params.netns = -1; + /* Create a temporary struct on the stack, to avoid allocating and freeing one. */ + meshlink_open_params_t params = { + .confbase = (char *)confbase, + .lock_filename = lock_filename, + .name = (char *)name, + .appname = (char *)appname, + .devclass = devclass, + .netns = -1, + }; if(!meshlink_open_params_set_storage_key(¶ms, key, keylen)) { return false; @@ -1442,13 +1463,12 @@ meshlink_handle_t *meshlink_open_ephemeral(const char *name, const char *appname } /* Create a temporary struct on the stack, to avoid allocating and freeing one. */ - meshlink_open_params_t params; - memset(¶ms, 0, sizeof(params)); - - params.name = (char *)name; - params.appname = (char *)appname; - params.devclass = devclass; - params.netns = -1; + meshlink_open_params_t params = { + .name = (char *)name, + .appname = (char *)appname, + .devclass = devclass, + .netns = -1, + }; return meshlink_open_ex(¶ms); } @@ -1548,7 +1568,7 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { meshlink_queue_init(&mesh->outpacketqueue); // Atomically lock the configuration directory. - if(!main_config_lock(mesh)) { + if(!main_config_lock(mesh, params->lock_filename)) { meshlink_close(mesh); return NULL; } @@ -1920,25 +1940,27 @@ void meshlink_close(meshlink_handle_t *mesh) { free(mesh); } -bool meshlink_destroy(const char *confbase) { - if(!confbase) { +bool meshlink_destroy_ex(const meshlink_open_params_t *params) { + if(!params) { meshlink_errno = MESHLINK_EINVAL; return false; } + if(!params->confbase) { + /* Ephemeral instances */ + return true; + } + /* Exit early if the confbase directory itself doesn't exist */ - if(access(confbase, F_OK) && errno == ENOENT) { + if(access(params->confbase, F_OK) && errno == ENOENT) { return true; } /* Take the lock the same way meshlink_open() would. */ - char lockfilename[PATH_MAX]; - snprintf(lockfilename, sizeof(lockfilename), "%s" SLASH "meshlink.lock", confbase); - - FILE *lockfile = fopen(lockfilename, "w+"); + FILE *lockfile = fopen(params->lock_filename, "w+"); if(!lockfile) { - logger(NULL, MESHLINK_ERROR, "Could not open lock file %s: %s", lockfilename, strerror(errno)); + logger(NULL, MESHLINK_ERROR, "Could not open lock file %s: %s", params->lock_filename, strerror(errno)); meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1952,7 +1974,7 @@ bool meshlink_destroy(const char *confbase) { #else if(flock(fileno(lockfile), LOCK_EX | LOCK_NB) != 0) { - logger(NULL, MESHLINK_ERROR, "Configuration directory %s still in use\n", lockfilename); + logger(NULL, MESHLINK_ERROR, "Configuration directory %s still in use\n", params->lock_filename); fclose(lockfile); meshlink_errno = MESHLINK_EBUSY; return false; @@ -1960,13 +1982,13 @@ bool meshlink_destroy(const char *confbase) { #endif - if(!config_destroy(confbase, "current") || !config_destroy(confbase, "new") || !config_destroy(confbase, "old")) { - logger(NULL, MESHLINK_ERROR, "Cannot remove sub-directories in %s: %s\n", confbase, strerror(errno)); + if(!config_destroy(params->confbase, "current") || !config_destroy(params->confbase, "new") || !config_destroy(params->confbase, "old")) { + logger(NULL, MESHLINK_ERROR, "Cannot remove sub-directories in %s: %s\n", params->confbase, strerror(errno)); return false; } - if(unlink(lockfilename)) { - logger(NULL, MESHLINK_ERROR, "Cannot remove lock file %s: %s\n", lockfilename, strerror(errno)); + if(unlink(params->lock_filename)) { + logger(NULL, MESHLINK_ERROR, "Cannot remove lock file %s: %s\n", params->lock_filename, strerror(errno)); fclose(lockfile); meshlink_errno = MESHLINK_ESTORAGE; return false; @@ -1974,8 +1996,8 @@ bool meshlink_destroy(const char *confbase) { fclose(lockfile); - if(!sync_path(confbase)) { - logger(NULL, MESHLINK_ERROR, "Cannot sync directory %s: %s\n", confbase, strerror(errno)); + if(!sync_path(params->confbase)) { + logger(NULL, MESHLINK_ERROR, "Cannot sync directory %s: %s\n", params->confbase, strerror(errno)); meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1983,6 +2005,18 @@ bool meshlink_destroy(const char *confbase) { return true; } +bool meshlink_destroy(const char *confbase) { + char lock_filename[PATH_MAX]; + snprintf(lock_filename, sizeof(lock_filename), "%s" SLASH "meshlink.lock", confbase); + + meshlink_open_params_t params = { + .confbase = (char *)confbase, + .lock_filename = lock_filename, + }; + + return meshlink_destroy_ex(¶ms); +} + void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) { if(!mesh) { meshlink_errno = MESHLINK_EINVAL; diff --git a/src/meshlink.h b/src/meshlink.h index 7960d310..ead75205 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -206,6 +206,17 @@ bool meshlink_open_params_set_storage_key(meshlink_open_params_t *params, const */ bool meshlink_open_params_set_storage_policy(meshlink_open_params_t *params, meshlink_storage_policy_t policy) __attribute__((__warn_unused_result__)); +/// Set the filename of the lockfile. +/** This function changes the path of the lockfile used to ensure only one instance of MeshLink can be open at the same time. + * If an application changes this, it must always set it to the same location. + * + * @param params A pointer to a meshlink_open_params_t which must have been created earlier with meshlink_open_params_init(). + * @param filename The filename of the lockfile. + * + * @return This function will return true if the open parameters have been successfully updated, false otherwise. + */ +bool meshlink_open_params_set_lock_filename(meshlink_open_params_t *params, const char *filename) __attribute__((__warn_unused_result__)); + /// Open or create a MeshLink instance. /** This function opens or creates a MeshLink instance. * All parameters needed by MeshLink are passed via a meshlink_open_params_t struct, @@ -379,6 +390,21 @@ void meshlink_close(struct meshlink_handle *mesh); */ bool meshlink_destroy(const char *confbase) __attribute__((__warn_unused_result__)); +/// Destroy a MeshLink instance using open parameters. +/** This function remove all configuration files of a MeshLink instance. It should only be called when the application + * does not have an open handle to this instance. Afterwards, a call to meshlink_open() will create a completely + * new instance. + * + * This version expects a pointer to meshlink_open_params_t, + * and will use exactly the same settings used for opening a handle to destroy it. + * + * @param params A pointer to a meshlink_open_params_t which must be filled in by the application. + * After the function returns, the application is free to reuse or free @a params. + * + * @return This function will return true if the MeshLink instance was successfully destroyed, false otherwise. + */ +bool meshlink_destroy_ex(const meshlink_open_params_t *params) __attribute__((__warn_unused_result__)); + /// A callback for receiving data from the mesh. /** @param mesh A handle which represents an instance of MeshLink. * @param source A pointer to a struct meshlink_node describing the source of the data. diff --git a/src/meshlink.sym b/src/meshlink.sym index f8df23e1..4ecd23e9 100644 --- a/src/meshlink.sym +++ b/src/meshlink.sym @@ -30,6 +30,7 @@ meshlink_clear_canonical_address meshlink_clear_invitation_addresses meshlink_close meshlink_destroy +meshlink_destroy_ex meshlink_enable_discovery meshlink_encrypted_key_rotate meshlink_errno @@ -65,6 +66,7 @@ meshlink_open_ephemeral meshlink_open_ex meshlink_open_params_free meshlink_open_params_init +meshlink_open_params_set_lock_filename meshlink_open_params_set_netns meshlink_open_params_set_storage_key meshlink_open_params_set_storage_policy diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 6cbe6093..d9000feb 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -59,6 +59,7 @@ typedef struct listen_socket_t { struct meshlink_open_params { char *confbase; + char *lock_filename; char *appname; char *name; dev_class_t devclass; -- 2.39.2