From e8a3882176247c1489502719a6f0181f95e1e737 Mon Sep 17 00:00:00 2001 From: Chalith Desaman Date: Tue, 5 Jan 2021 11:18:23 +0530 Subject: [PATCH] Prevent running multiple hpcore instances from the same directory (#207) --- src/conf.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++- src/conf.hpp | 13 ++++++++-- src/main.cpp | 1 + src/util/util.cpp | 29 ++++++++++++++++++++++ src/util/util.hpp | 3 +++ 5 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/conf.cpp b/src/conf.cpp index 083b7724..c44ae951 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -24,6 +24,8 @@ namespace conf const static char *PATCH_FILE_NAME = "patch.cfg"; // Config patch filename. + bool init_success = false; + /** * Loads and initializes the contract config for execution. Must be called once during application startup. * @return 0 for success. -1 for failure. @@ -35,23 +37,42 @@ namespace conf // 2. Read and load the contract config into memory // 3. Update contract config if patch file exists. // 4. Validate the loaded config values + // 5. Locking the config file at the startup. if (validate_contract_dir_paths() == -1 || read_config(cfg) == -1 || apply_patch_changes(cfg.contract) == -1 || - validate_config(cfg) == -1) + validate_config(cfg) == -1 || + set_config_lock() == -1) { return -1; } + init_success = true; return 0; } + /** + * Cleanup any resources. + */ + void deinit() + { + if (init_success) + { + // Releases the config file lock at the termination. + release_config_lock(); + } + } + /** * Generates and saves new signing keys in the contract config. */ int rekey() { + // Locking the config file at the startup. To check whether there's any already running hp instances. + if (set_config_lock() == -1) + return -1; + // Load the contract config and re-save with the newly generated keys. contract_config cfg = {}; if (read_config(cfg) != 0) @@ -66,6 +87,9 @@ namespace conf std::cout << "New signing keys generated at " << ctx.config_file << std::endl; + // Releases the config file lock at the termination. + release_config_lock(); + return 0; } @@ -874,4 +898,41 @@ namespace conf } return 0; } + + /** + * Locks the config file. If already locked means there's another hpcore instance running in the same directory. + * If so, log error and return, Otherwise lock the config. + * @return Returns 0 if lock is successfully aquired, -1 on error. + */ + int set_config_lock() + { + ctx.config_fd = open(ctx.config_file.data(), O_RDWR, 444); + if (ctx.config_fd == -1) + return -1; + + if (util::set_lock(ctx.config_fd, ctx.config_lock, true, 0, 0) == -1) + { + if (errno == EACCES || errno == EAGAIN) + { + std::cerr << "Another hpcore instance is already running in directory " << ctx.contract_dir << "\n"; + } + // Close fd if lock aquiring failed. + close(ctx.config_fd); + return -1; + } + + return 0; + } + + /** + * Releses the config file and closes the opened file descriptor. + * @return Returns 0 if lock is successfully aquired, -1 on error. + */ + int release_config_lock() + { + const int res = util::release_lock(ctx.config_fd, ctx.config_lock); + // Close fd in termination. + close(ctx.config_fd); + return res; + } } // namespace conf diff --git a/src/conf.hpp b/src/conf.hpp index 910ef1d0..6bea510f 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -65,9 +65,9 @@ namespace conf std::string public_key; // Contract public key bytes std::string private_key; // Contract private key bytes ROLE role = ROLE::OBSERVER; // Configured startup role of the contract (Observer/validator). - bool is_unl = false; // Indicate whether we are a unl node or not. + bool is_unl = false; // Indicate whether we are a unl node or not. - std::string public_key_hex; // Contract hex public key + std::string public_key_hex; // Contract hex public key std::string private_key_hex; // Contract hex private key bool full_history = false; // Whether full history mode is on/off. }; @@ -147,6 +147,9 @@ namespace conf std::string config_file; // Full path to the contract config file std::string tls_key_file; // Full path to the tls private key file std::string tls_cert_file; // Full path to the tls certificate + + int config_fd; // Config file file descriptor + struct flock config_lock; // Config file record log }; // Holds all the contract config values. @@ -171,6 +174,8 @@ namespace conf int init(); + void deinit(); + int rekey(); int create_contract(); @@ -198,6 +203,10 @@ namespace conf int apply_patch_changes(contract_params &contract_config); int validate_and_apply_patch_config(contract_params &contract_config, std::string_view mount_dir); + + int set_config_lock(); + + int release_config_lock(); } // namespace conf #endif diff --git a/src/main.cpp b/src/main.cpp index d48bc0e3..f6f0c66d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,6 +79,7 @@ void deinit() sc::deinit(); unl::deinit(); ledger::deinit(); + conf::deinit(); } void sig_exit_handler(int signum) diff --git a/src/util/util.cpp b/src/util/util.cpp index b557abea..3da38c9b 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -355,4 +355,33 @@ namespace util return 0; } + /** + * Create a record lock for the file descriptor. Lock is associated with the process (Not for forked child processes). + * @param fd File descriptor to be locked. + * @param lock File lock. + * @param is_rwlock Whether the record lock is a write lock. + * @param start Starting offset for the lock. + * @param len Number of bytes to lock. + * @return Returns 0 if lock is successfully aquired, -1 on error. + */ + int set_lock(const int fd, struct flock &lock, const bool is_rwlock, const off_t start, const off_t len) + { + lock.l_type = is_rwlock ? F_WRLCK : F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = start, + lock.l_len = len; + return fcntl(fd, F_SETLK, &lock); + } + + /** + * Releases the lock on file descriptor. + * @param fd File descriptor to be released. + * @param lock File lock. + * @return Returns 0 if lock is successfully released, -1 on error. + */ + int release_lock(const int fd, struct flock &lock) + { + lock.l_type = F_UNLCK; + return fcntl(fd, F_SETLKW, &lock); + } } // namespace util diff --git a/src/util/util.hpp b/src/util/util.hpp index b6fbb7d2..352cc6c8 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -78,6 +78,9 @@ namespace util int stoull(const std::string &str, uint64_t &result); + int set_lock(const int fd, struct flock &lock, const bool is_rwlock, const off_t start, const off_t len); + + int release_lock(const int fd, struct flock &lock); } // namespace util #endif