From 0653b51d916627fab16797f66009eea61feb1a8c Mon Sep 17 00:00:00 2001 From: Alberto Miranda Date: Wed, 29 Mar 2023 11:28:48 +0200 Subject: [PATCH 1/2] CMake: Look for `srun` binary --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2020f674..3e63aa84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Check for and/or download dependencies # ############################################################################## +### srun is required in order to deploy `scord-ctl` and `cargo` +find_program(SRUN srun REQUIRED) + ### some dependencies don't provide CMake modules, but rely on pkg-config ### instead, make sure that pkg-config is available find_package(PkgConfig REQUIRED) -- GitLab From ccf1a6237270e9892028860e15a0f4d3a36c8504 Mon Sep 17 00:00:00 2001 From: Alberto Miranda Date: Fri, 14 Apr 2023 13:07:35 +0200 Subject: [PATCH 2/2] Refactor configuration for scord and scord-ctl scord: - Configuration file now uses standard YAML format scord-ctl: - Add `--output` CLI argument - Add `--listen` CLI argument (Fixes #126) - Remove configuration file Both: - Remove file_options dependency - Remove genopts dependency - Replace yaml-cpp with rapidyaml - Replace general settings with specific configurations (fixes #20) - General improvements and bugfixes in `logger` - Improved `net::server` configuration - Fix fork()-related bug in `signal_listener` - Remove environment variables --- CMakeLists.txt | 30 ++- etc/scord.conf.in | 21 +- src/common/CMakeLists.txt | 4 - src/common/config/CMakeLists.txt | 87 -------- src/common/config/config.hpp | 31 --- src/common/config/defaults.cpp.in | 52 ----- src/common/config/defaults.hpp | 53 ----- src/common/config/file_options.yml | 41 ---- src/common/config/genopts.yml.in | 14 -- src/common/config/parsers.cpp | 103 --------- src/common/config/parsers.hpp | 49 ----- src/common/config/settings.cpp | 278 ----------------------- src/common/config/settings.hpp | 232 -------------------- src/common/logger/logger.cpp | 18 +- src/common/logger/logger.h | 8 +- src/common/logger/logger.hpp | 86 ++++++-- src/common/net/CMakeLists.txt | 4 +- src/common/net/server.cpp | 115 +++++----- src/common/net/server.hpp | 36 ++- src/common/utils/signal_listener.hpp | 7 + src/lib/libscord.cpp | 8 +- src/scord-ctl/CMakeLists.txt | 6 +- src/scord-ctl/env.hpp | 39 ---- src/scord-ctl/scord-ctl.cpp | 169 ++++---------- src/scord/CMakeLists.txt | 28 ++- src/scord/{env.hpp => defaults.hpp.in} | 20 +- src/scord/scord.cpp | 293 +++++++++++++------------ 27 files changed, 412 insertions(+), 1420 deletions(-) delete mode 100644 src/common/config/CMakeLists.txt delete mode 100644 src/common/config/config.hpp delete mode 100644 src/common/config/defaults.cpp.in delete mode 100644 src/common/config/defaults.hpp delete mode 100644 src/common/config/file_options.yml delete mode 100644 src/common/config/genopts.yml.in delete mode 100644 src/common/config/parsers.cpp delete mode 100644 src/common/config/parsers.hpp delete mode 100644 src/common/config/settings.cpp delete mode 100644 src/common/config/settings.hpp delete mode 100644 src/scord-ctl/env.hpp rename src/scord/{env.hpp => defaults.hpp.in} (71%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e63aa84..d719ca87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,29 +223,27 @@ FetchContent_Declare( FetchContent_MakeAvailable(spdlog) set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE ON) -### file_options: required for reading configuration files -message(STATUS "[${PROJECT_NAME}] Downloading and building file_options") +### rapidyaml: required for reading configuration files +message(STATUS "[${PROJECT_NAME}] Downloading and building rapidyaml") FetchContent_Declare( - file_options - GIT_REPOSITORY https://storage.bsc.es/gitlab/utils/file_options - GIT_TAG bdb4f7f7f2dd731815241fc41afe6373df8f732a # v0.1.0-pre - GIT_SHALLOW ON - GIT_PROGRESS ON + ryml + GIT_REPOSITORY https://github.com/biojppm/rapidyaml.git + GIT_TAG b35ccb150282760cf5c2d316895cb86bd161ac89 #v0.5.0 + GIT_SHALLOW OFF # ensure submodules are checked out ) -FetchContent_MakeAvailable(file_options) +FetchContent_MakeAvailable(ryml) +set_target_properties(ryml PROPERTIES POSITION_INDEPENDENT_CODE ON) -### genopts: required for generating file_options schemas -message(STATUS "[${PROJECT_NAME}] Downloading and building genopts") -FetchContent_Declare( - genopts - GIT_REPOSITORY https://storage.bsc.es/gitlab/utils/genopts - GIT_TAG c456c2d8ec92f26d9074b123446261103e5c847c # v0.1.0-pre +### CLI11: used for parsing command-line options +message(STATUS "[${PROJECT_NAME}] Searching for CLI11") +FetchContent_Declare(cli11 + GIT_REPOSITORY https://github.com/CLIUtils/CLI11 + GIT_TAG 291c58789c031208f08f4f261a858b5b7083e8e2 # v2.3.2 GIT_SHALLOW ON GIT_PROGRESS ON ) - -FetchContent_MakeAvailable(genopts) +FetchContent_MakeAvailable(cli11) ### expected: required for using tl::expected in the C++ library implementation ### until std::expected makes it to C++ diff --git a/etc/scord.conf.in b/etc/scord.conf.in index 4e2c26d1..0efad292 100644 --- a/etc/scord.conf.in +++ b/etc/scord.conf.in @@ -1,26 +1,13 @@ ## vim: set filetype=yaml: ## global service settings -global_settings: [ - - # if true, dump log messages to syslog - use_syslog: false, +global_settings: # log file - log_file: "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/@CMAKE_PROJECT_NAME@/@CMAKE_PROJECT_NAME@.log", + logfile: "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/@CMAKE_PROJECT_NAME@/@CMAKE_PROJECT_NAME@.log" # path to pidfile - pidfile: "@CMAKE_INSTALL_FULL_RUNSTATEDIR@/@CMAKE_PROJECT_NAME@/@CMAKE_PROJECT_NAME@.pid", - - # transport protocol used for communication - transport_protocol: "@SCORD_TRANSPORT_PROTOCOL@", + rundir: "@CMAKE_INSTALL_FULL_RUNSTATEDIR@/@CMAKE_PROJECT_NAME@" # address to bind to - bind_address: "@SCORD_BIND_ADDRESS@", - - # incoming port for remote connections - remote_port: @SCORD_BIND_PORT@, - - # number of worker threads to serve I/O requests - workers: 4, -] + address: "@SCORD_TRANSPORT_PROTOCOL@://@SCORD_BIND_ADDRESS@:@SCORD_BIND_PORT@" diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9aecbf56..ae53e3cd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -29,10 +29,6 @@ add_subdirectory(utils) target_include_directories(_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) add_library(common::utils ALIAS _utils) -add_subdirectory(config) -target_include_directories(_config INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -add_library(common::config ALIAS _config) - add_subdirectory(logger) target_include_directories(_logger INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) add_library(common::logger ALIAS _logger) diff --git a/src/common/config/CMakeLists.txt b/src/common/config/CMakeLists.txt deleted file mode 100644 index fa464f06..00000000 --- a/src/common/config/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Copyright 2021, Barcelona Supercomputing Center (BSC), Spain # -# # -# This software was partially supported by the EuroHPC-funded project ADMIRE # -# (Project ID: 956748, https://www.admire-eurohpc.eu). # -# # -# This file is part of scord. # -# # -# scord is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# scord is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with scord. If not, see . # -# # -# SPDX-License-Identifier: GPL-3.0-or-later # -################################################################################ - -# Create a config target for all configuration code -add_library(_config STATIC) - -# Since some of the sources will be auto-generated, we need to search for -# includes in ${CMAKE_CURRENT_BINARY_DIR} -target_include_directories( - _config PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} -) -set_property(TARGET _config PROPERTY POSITION_INDEPENDENT_CODE ON) - -target_sources( - _config - PRIVATE config.hpp - defaults.hpp - ${CMAKE_CURRENT_BINARY_DIR}/defaults.cpp - ${CMAKE_CURRENT_BINARY_DIR}/config_options.hpp - ${CMAKE_CURRENT_BINARY_DIR}/keywords.hpp - parsers.cpp - parsers.hpp - settings.cpp - settings.hpp -) - -target_link_libraries(_config PRIVATE common::utils file_options::file_options) - -# ############################################################################## -# Produce several auto-generated files for 'config' -# ############################################################################## - -# Default values for options -configure_file(defaults.cpp.in defaults.cpp) - -# Automatic generation of file_options schemas. To facilitate the management of -# any file-based configuration options, we generate a C++ schema from a YAML -# file that allows the 'file_options' library to parse them fromm a -# configuration file. The process works as follows: -# 1. We define the desired options in 'file_options.yml' -# 2. We rely on the 'genopts' tool to generate valid C++ schemas for the -# 'file_options' library. This tool can be configured using a 'genopts.yml' -# configuration file. -# 3. In the daemon code, we use the facilities provided by the 'file_options' -# library to access the options values. - -# Since the configuration in genopts.yml can be path-dependant, we rely on -# CMake to substitute any special @variables@ for their actual values -configure_file(genopts.yml.in genopts.yml @ONLY) - -# Define the command that will generate config_options.hpp and keywords.hpp. -# It will be executed since there is a direct dependency between 'config' -# (defined below) and these output files. -# We also make the command depend on file_options.yml and genopts.yml so that -# it gets re-executed if they change. -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config_options.hpp - ${CMAKE_CURRENT_BINARY_DIR}/keywords.hpp - COMMENT "Generating config_options.hpp, keywords.hpp" - DEPENDS genopts_virtualenv file_options.yml ${CMAKE_CURRENT_BINARY_DIR}/genopts.yml - COMMAND - Genopts::Python3_Interpreter -m genopts --config - ${CMAKE_CURRENT_BINARY_DIR}/genopts.yml - ${CMAKE_CURRENT_LIST_DIR}/file_options.yml -) diff --git a/src/common/config/config.hpp b/src/common/config/config.hpp deleted file mode 100644 index 5bae57e1..00000000 --- a/src/common/config/config.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#ifndef SCORD_CONFIG_HPP -#define SCORD_CONFIG_HPP - -#include "defaults.hpp" -#include "settings.hpp" - -#endif /* SCORD_CONFIG_HPP */ diff --git a/src/common/config/defaults.cpp.in b/src/common/config/defaults.cpp.in deleted file mode 100644 index bfd670aa..00000000 --- a/src/common/config/defaults.cpp.in +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -/* DO NOT MODIFY: This file is autogenerated by CMake */ -#include -#include -#include -#include -#include - -namespace scord::config::defaults { - -const char* progname = "@CMAKE_PROJECT_NAME@"; -const bool daemonize = true; -const bool use_syslog = false; -const bool use_console = false; -const std::filesystem::path log_file = {}; -const uint32_t log_file_max_size = static_cast(16 * 1024 * 1024); - -const char* transport_protocol = "ofi+tcp"; -const char* bind_address = "127.0.0.1"; -const in_port_t remote_port = 42000; -const char* pidfile = - "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/@CMAKE_PROJECT_NAME@.pid"; - -const uint32_t workers_in_pool = std::thread::hardware_concurrency(); -const uint32_t backlog_size = 128; -const char* config_file = - "@CMAKE_INSTALL_FULL_SYSCONFDIR@/@CMAKE_PROJECT_NAME@.conf"; - -} // namespace scord::config::defaults diff --git a/src/common/config/defaults.hpp b/src/common/config/defaults.hpp deleted file mode 100644 index fe55ab23..00000000 --- a/src/common/config/defaults.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#ifndef SCORD_CONFIG_DEFAULTS_HPP -#define SCORD_CONFIG_DEFAULTS_HPP - -#include -#include -#include - -namespace fs = std::filesystem; - -namespace scord::config::defaults { - -extern const char* progname; -extern const bool daemonize; -extern const bool use_syslog; -extern const bool use_console; -extern const fs::path log_file; -extern const uint32_t log_file_max_size; -extern const char* transport_protocol; -extern const char* bind_address; -extern const in_port_t remote_port; -extern const char* pidfile; -extern const uint32_t workers_in_pool; -extern const char* staging_directory; -extern const uint32_t backlog_size; -extern const char* config_file; - -} // namespace scord::config::defaults - -#endif /* SCORD_CONFIG_DEFAULTS_HPP */ diff --git a/src/common/config/file_options.yml b/src/common/config/file_options.yml deleted file mode 100644 index 7049676e..00000000 --- a/src/common/config/file_options.yml +++ /dev/null @@ -1,41 +0,0 @@ -sections: - - name: "global_settings" - required: true - options: - - name: "use_syslog" - required: true - type: "bool" - converter: "parsers::parse_bool" - - - name: "log_file" - required: false - type: "std::filesystem::path" - converter: "parsers::parse_path" - - - name: "log_file_max_size" - required: false - type: "uint32_t" - converter: "parsers::parse_capacity" - - - name: "transport_protocol" - required: true - type: "std::string" - - - name: "bind_address" - required: true - type: "std::string" - - - name: "remote_port" - required: true - type: "uint32_t" - converter: "parsers::parse_number" - - - name: "pidfile" - required: true - type: "std::filesystem::path" - converter: "parsers::parse_path" - - - name: "workers" - required: true - type: "uint32_t" - converter: "parsers::parse_number" diff --git a/src/common/config/genopts.yml.in b/src/common/config/genopts.yml.in deleted file mode 100644 index fae2d629..00000000 --- a/src/common/config/genopts.yml.in +++ /dev/null @@ -1,14 +0,0 @@ -genopts: - schema: - copyright_file: "@CMAKE_SOURCE_DIR@/COPYRIGHT_NOTICE" - header_guard: "SCORD_CONFIG_SCHEMA_HPP" - namespace: "scord::config" - output_path: - header: "@CMAKE_CURRENT_BINARY_DIR@/config_options.hpp" - - keywords: - copyright_file: "@CMAKE_SOURCE_DIR@/COPYRIGHT_NOTICE" - header_guard: "SCORD_CONFIG_KEYWORDS_HPP" - namespace: "scord::config::keywords" - output_path: - header: "@CMAKE_CURRENT_BINARY_DIR@/keywords.hpp" diff --git a/src/common/config/parsers.cpp b/src/common/config/parsers.cpp deleted file mode 100644 index a45d853a..00000000 --- a/src/common/config/parsers.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include "parsers.hpp" - -namespace fs = std::filesystem; - -namespace scord::config::parsers { - -bool -parse_bool(const std::string& name, const std::string& value) { - - if(value == "1" || boost::algorithm::to_lower_copy(value) == "true") { - return true; - } - - if(value == "0" || boost::algorithm::to_lower_copy(value) == "false") { - return false; - } - - throw std::invalid_argument("Value provided for option '" + name + - "' is not boolean"); -} - -uint32_t -parse_number(const std::string& name, const std::string& value) { - - int32_t optval = 0; - - try { - optval = std::stoi(value); - } catch(...) { - throw std::invalid_argument("Value provided for option '" + name + - "' is not a number"); - } - - if(optval <= 0) { - throw std::invalid_argument("Value provided for option '" + name + - "' must be greater than zero"); - } - - return static_cast(optval); -} - -fs::path -parse_path(const std::string& name, const std::string& value) { - - (void) name; - - return {value}; -} - -fs::path -parse_existing_path(const std::string& name, const std::string& value) { - - if(!fs::exists(value)) { - throw std::invalid_argument("Path '" + value + "' in option '" + name + - "' does not exist"); - } - - return {value}; -} - -uint64_t -parse_capacity(const std::string& name, const std::string& value) { - - try { - return scord::utils::parse_size(value); - } catch(const std::exception& e) { - throw std::invalid_argument("Value provided in option '" + name + - "' is invalid"); - } -} - -} // namespace scord::config::parsers diff --git a/src/common/config/parsers.hpp b/src/common/config/parsers.hpp deleted file mode 100644 index db020863..00000000 --- a/src/common/config/parsers.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#ifndef SCORD_CONFIG_PARSERS_HPP -#define SCORD_CONFIG_PARSERS_HPP - -#include -#include -#include - -namespace fs = std::filesystem; - -namespace scord::config::parsers { - -bool -parse_bool(const std::string& name, const std::string& value); -uint32_t -parse_number(const std::string& name, const std::string& value); -fs::path -parse_path(const std::string& name, const std::string& value); -fs::path -parse_existing_path(const std::string& name, const std::string& value); -uint64_t -parse_capacity(const std::string& name, const std::string& value); - -} // namespace scord::config::parsers - -#endif // SCORD_CONFIG_PARSERS_HPP diff --git a/src/common/config/settings.cpp b/src/common/config/settings.cpp deleted file mode 100644 index 0987446d..00000000 --- a/src/common/config/settings.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#include -#include -#include "config_options.hpp" -#include "defaults.hpp" -#include "file_options/options_description.hpp" -#include "file_options/yaml_parser.hpp" -#include "keywords.hpp" -#include "settings.hpp" - -namespace fs = std::filesystem; - -namespace scord::config { - -settings::settings() = default; - -settings::settings(std::string progname, bool daemonize, bool use_syslog, - bool use_console, fs::path log_file, - const uint32_t log_file_max_size, - std::string transport_protocol, std::string bind_address, - uint32_t remote_port, fs::path pidfile, uint32_t workers, - uint32_t backlog_size, fs::path cfgfile, - std::list defns) - : m_progname(std::move(progname)), m_daemonize(daemonize), - m_use_syslog(use_syslog), m_use_console(use_console), - m_log_file(std::move(log_file)), m_log_file_max_size(log_file_max_size), - m_transport_protocol(std::move(transport_protocol)), - m_bind_address(std::move(bind_address)), m_remote_port(remote_port), - m_daemon_pidfile(std::move(pidfile)), m_workers_in_pool(workers), - m_backlog_size(backlog_size), m_config_file(std::move(cfgfile)), - m_default_namespaces(std::move(defns)) {} - -void -settings::load_defaults() { - m_progname = defaults::progname; - m_daemonize = defaults::daemonize; - m_use_syslog = defaults::use_syslog; - m_use_console = defaults::use_console; - m_log_file = defaults::log_file; - m_log_file_max_size = defaults::log_file_max_size; - m_transport_protocol = defaults::transport_protocol; - m_bind_address = defaults::bind_address; - m_remote_port = defaults::remote_port; - m_daemon_pidfile = defaults::pidfile; - m_workers_in_pool = defaults::workers_in_pool; - m_backlog_size = defaults::backlog_size; - m_config_file = defaults::config_file; - m_default_namespaces.clear(); -} - -void -settings::load_from_file(const fs::path& filename) { - file_options::options_map opt_map; - file_options::parse_yaml_file(filename, config::valid_options, opt_map); - - // load global settings - const auto& gsettings = opt_map.get_as( - keywords::global_settings); - - m_progname = defaults::progname; - m_use_syslog = gsettings.get_as(keywords::use_syslog); - m_use_console = defaults::use_console; - - if(gsettings.has(keywords::log_file)) { - m_log_file = gsettings.get_as(keywords::log_file); - } - - if(gsettings.has(keywords::log_file_max_size)) { - m_log_file_max_size = - gsettings.get_as(keywords::log_file_max_size); - } - - m_transport_protocol = - gsettings.get_as(keywords::transport_protocol); - m_bind_address = gsettings.get_as(keywords::bind_address); - m_remote_port = gsettings.get_as(keywords::remote_port); - m_daemon_pidfile = gsettings.get_as(keywords::pidfile); - m_workers_in_pool = gsettings.get_as(keywords::workers); - m_backlog_size = defaults::backlog_size; - - // load definitions for default namespaces - // const auto& namespaces = - // opt_map.get_as(keywords::namespaces); - // - // for(const auto& nsdef : namespaces) { - // m_default_namespaces.emplace_back( - // nsdef.get_as(keywords::nsid), - // nsdef.get_as(keywords::track_contents), - // nsdef.get_as(keywords::mountpoint), - // nsdef.get_as(keywords::type), - // nsdef.get_as(keywords::capacity), - // nsdef.get_as(keywords::visibility)); - // } -} - -std::string -settings::to_string() const { - std::string str = - std::string("settings {\n") + " m_progname: " + m_progname + - ",\n" + " m_daemonize: " + (m_daemonize ? "true" : "false") + - ",\n" + " m_use_syslog: " + (m_use_syslog ? "true" : "false") + - ",\n" + " m_use_console: " + (m_use_console ? "true" : "false") + - ",\n" + " m_log_file: " + m_log_file.string() + ",\n" + - " m_log_file_max_size: " + std::to_string(m_log_file_max_size) + - ",\n" + " m_bind_address: " + m_bind_address + ",\n" + - " m_remote_port: " + std::to_string(m_remote_port) + ",\n" + - " m_pidfile: " + m_daemon_pidfile.string() + ",\n" + - " m_workers: " + std::to_string(m_workers_in_pool) + ",\n" + - " m_backlog_size: " + std::to_string(m_backlog_size) + ",\n" + - " m_config_file: " + m_config_file.string() + ",\n" + "};"; - // TODO: add m_default_namespaces - return str; -} - -std::string -settings::progname() const { - return m_progname; -} - -void -settings::progname(const std::string& progname) { - m_progname = progname; -} - -bool -settings::daemonize() const { - return m_daemonize; -} - -void -settings::daemonize(bool daemonize) { - m_daemonize = daemonize; -} - -bool -settings::use_syslog() const { - return m_use_syslog; -} - -void -settings::use_syslog(bool use_syslog) { - m_use_syslog = use_syslog; -} - -bool -settings::use_console() const { - return m_use_console; -} - -void -settings::use_console(bool use_console) { - m_use_console = use_console; -} - -fs::path -settings::log_file() const { - return m_log_file; -} - -void -settings::log_file(const fs::path& log_file) { - m_log_file = log_file; -} - -uint32_t -settings::log_file_max_size() const { - return m_log_file_max_size; -} - -void -settings::log_file_max_size(uint32_t log_file_max_size) { - m_log_file_max_size = log_file_max_size; -} - -std::string -settings::transport_protocol() const { - return m_transport_protocol; -} -void -settings::transport_protocol(const std::string& transport_protocol) { - m_transport_protocol = transport_protocol; -} - -std::string -settings::bind_address() const { - return m_bind_address; -} - -void -settings::bind_address(const std::string& bind_address) { - m_bind_address = bind_address; -} - -in_port_t -settings::remote_port() const { - return m_remote_port; -} - -void -settings::remote_port(in_port_t remote_port) { - m_remote_port = remote_port; -} - -fs::path -settings::pidfile() const { - return m_daemon_pidfile; -} - -void -settings::pidfile(const fs::path& pidfile) { - m_daemon_pidfile = pidfile; -} - -uint32_t -settings::workers_in_pool() const { - return m_workers_in_pool; -} - -void -settings::workers_in_pool(uint32_t workers_in_pool) { - m_workers_in_pool = workers_in_pool; -} - -uint32_t -settings::backlog_size() const { - return m_backlog_size; -} - -void -settings::backlog_size(uint32_t backlog_size) { - m_backlog_size = backlog_size; -} - -fs::path -settings::config_file() const { - return m_config_file; -} - -void -settings::config_file(const fs::path& config_file) { - m_config_file = config_file; -} - -std::list -settings::default_namespaces() const { - return m_default_namespaces; -} - -void -settings::default_namespaces( - const std::list& default_namespaces) { - m_default_namespaces = default_namespaces; -} - -} // namespace scord::config diff --git a/src/common/config/settings.hpp b/src/common/config/settings.hpp deleted file mode 100644 index e64bbec5..00000000 --- a/src/common/config/settings.hpp +++ /dev/null @@ -1,232 +0,0 @@ -/****************************************************************************** - * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#ifndef SCORD_CONFIG_SETTINGS_HPP -#define SCORD_CONFIG_SETTINGS_HPP - -#include -#include -#include -#include -#include -#include - -#include "defaults.hpp" - -namespace fs = std::filesystem; - -namespace scord::config { - -struct namespace_def { - - namespace_def(std::string nsid, bool track, fs::path mountpoint, - std::string alias, const uint64_t capacity, - std::string visibility) - : m_nsid(std::move(nsid)), m_track(track), - m_mountpoint(std::move(mountpoint)), m_alias(std::move(alias)), - m_capacity(capacity), m_visibility(std::move(visibility)) {} - - namespace_def(const namespace_def& other) = default; - - namespace_def(namespace_def&& rhs) = default; - - namespace_def& - operator=(const namespace_def& other) = default; - - namespace_def& - operator=(namespace_def&& rhs) = default; - - std::string - nsid() const { - return m_nsid; - } - - bool - track() const { - return m_track; - } - - fs::path - mountpoint() const { - return m_mountpoint; - } - - std::string - alias() const { - return m_alias; - } - - uint64_t - capacity() const { - return m_capacity; - } - - std::string - visibility() const { - return m_visibility; - } - - std::string m_nsid; - bool m_track; - fs::path m_mountpoint; - std::string m_alias; - uint64_t m_capacity; - std::string m_visibility; -}; - -struct settings { - - settings(); - - settings(std::string progname, bool daemonize, bool use_syslog, - bool use_console, fs::path log_file, - const uint32_t log_file_max_size, std::string transport_protocol, - std::string bind_address, uint32_t remote_port, fs::path pidfile, - uint32_t workers, uint32_t backlog_size, fs::path cfgfile, - std::list defns); - - void - load_defaults(); - - void - load_from_file(const fs::path& filename); - - std::string - to_string() const; - - settings(const settings& other) = default; - - settings(settings&& rhs) = default; - - settings& - operator=(const settings& other) = default; - - settings& - operator=(settings&& rhs) = default; - - ~settings() = default; - - std::string - progname() const; - - void - progname(const std::string& progname); - - bool - daemonize() const; - - void - daemonize(bool daemonize); - - bool - use_syslog() const; - - void - use_syslog(bool use_syslog); - - bool - use_console() const; - - void - use_console(bool use_console); - - fs::path - log_file() const; - - void - log_file(const fs::path& log_file); - - uint32_t - log_file_max_size() const; - - void - log_file_max_size(uint32_t log_file_max_size); - - std::string - transport_protocol() const; - - void - transport_protocol(const std::string& transport_protocol); - - std::string - bind_address() const; - - void - bind_address(const std::string& bind_address); - - in_port_t - remote_port() const; - - void - remote_port(in_port_t remote_port); - - fs::path - pidfile() const; - - void - pidfile(const fs::path& pidfile); - - uint32_t - workers_in_pool() const; - - void - workers_in_pool(uint32_t workers_in_pool); - - uint32_t - backlog_size() const; - - void - backlog_size(uint32_t backlog_size); - - fs::path - config_file() const; - - void - config_file(const fs::path& config_file); - - std::list - default_namespaces() const; - - void - default_namespaces(const std::list& default_namespaces); - - std::string m_progname = defaults::progname; - bool m_daemonize = defaults::daemonize; - bool m_use_syslog = defaults::use_syslog; - bool m_use_console = defaults::use_console; - fs::path m_log_file = defaults::log_file; - uint32_t m_log_file_max_size = defaults::log_file_max_size; - std::string m_transport_protocol = defaults::transport_protocol; - std::string m_bind_address = defaults::bind_address; - in_port_t m_remote_port = defaults::remote_port; - fs::path m_daemon_pidfile = defaults::pidfile; - uint32_t m_workers_in_pool = defaults::workers_in_pool; - uint32_t m_backlog_size = defaults::backlog_size; - fs::path m_config_file = defaults::config_file; - std::list m_default_namespaces; -}; - -} // namespace scord::config - -#endif /* SCORD_CONFIG_SETTINGS_HPP */ diff --git a/src/common/logger/logger.cpp b/src/common/logger/logger.cpp index 4dcfef37..e46b295f 100644 --- a/src/common/logger/logger.cpp +++ b/src/common/logger/logger.cpp @@ -28,8 +28,22 @@ #include "logger.h" void -logger_setup(const char* ident, const char* type, const char* log_file) { - scord::logger::create_global_logger(ident, type, log_file); +logger_setup(const char* ident, logger_type type, const char* log_file) { + constexpr auto get_cxx_type = [](logger_type t) { + switch(t) { + case CONSOLE_LOGGER: + return scord::logger_type::console; + case CONSOLE_COLOR_LOGGER: + return scord::logger_type::console_color; + case FILE_LOGGER: + return scord::logger_type::file; + case SYSLOG_LOGGER: + return scord::logger_type::syslog; + default: + return scord::logger_type::console; + } + }; + scord::logger::create_global_logger(ident, get_cxx_type(type), log_file); } void diff --git a/src/common/logger/logger.h b/src/common/logger/logger.h index 35b8398b..c776dc79 100644 --- a/src/common/logger/logger.h +++ b/src/common/logger/logger.h @@ -33,6 +33,12 @@ extern "C" { #define LOGGER_MSG_MAX_LEN 2048 +enum logger_type { + CONSOLE_LOGGER, + CONSOLE_COLOR_LOGGER, + FILE_LOGGER, + SYSLOG_LOGGER +}; enum logger_level { info, debug, warn, error, critical }; /** @@ -48,7 +54,7 @@ enum logger_level { info, debug, warn, error, critical }; * @param log_file The path to the log file where messages should be written. */ void -setup_logger(const char* ident, const char* type, const char* log_file); +setup_logger(const char* ident, enum logger_type type, const char* log_file); /** * Emit a message. diff --git a/src/common/logger/logger.hpp b/src/common/logger/logger.hpp index 8f26f717..df565cfa 100644 --- a/src/common/logger/logger.hpp +++ b/src/common/logger/logger.hpp @@ -30,8 +30,10 @@ #include #include #include +#include #include #include +#include #include #include "macros.h" @@ -50,36 +52,74 @@ namespace fs = std::filesystem; namespace scord { +enum logger_type { + console, + console_color, + file, + syslog, +}; + +class logger_config { + +public: + logger_config() = default; + + explicit logger_config(std::string ident, scord::logger_type type, + std::optional log_file = {}) + : m_ident(std::move(ident)), m_type(type), + m_log_file(std::move(log_file)) {} + + const std::string& + ident() const { + return m_ident; + } + + scord::logger_type + type() const { + return m_type; + } + + const std::optional& + log_file() const { + return m_log_file; + } + +private: + std::string m_ident; + scord::logger_type m_type = console_color; + std::optional m_log_file; +}; class logger { public: - logger(const std::string& ident, const std::string& type, + logger(const std::string& ident, logger_type type, const fs::path& log_file = "") { try { - if(type == "console") { - m_internal_logger = - spdlog::stdout_logger_mt(ident); - } else if(type == "console color") { - m_internal_logger = - spdlog::stdout_color_mt(ident); - } else if(type == "file") { - m_internal_logger = - spdlog::basic_logger_mt( - ident, log_file.string(), true); - - } -#ifdef SPDLOG_ENABLE_SYSLOG - else if(type == "syslog") { - m_internal_logger = - spdlog::syslog_logger("syslog", ident, LOG_PID); - } -#endif - else { - throw std::invalid_argument("Unknown logger type: '" + type + - "'"); + switch(type) { + case console: + m_internal_logger = + spdlog::stdout_logger_mt( + ident); + break; + case console_color: + m_internal_logger = + spdlog::stdout_color_mt( + ident); + break; + case file: + m_internal_logger = + spdlog::basic_logger_mt( + ident, log_file.string(), true); + break; + case syslog: + m_internal_logger = + spdlog::syslog_logger_mt("syslog", ident, LOG_PID); + break; + default: + throw std::invalid_argument("Unknown logger type"); } assert(m_internal_logger != nullptr); @@ -122,7 +162,7 @@ public: operator=(logger&& /*other*/) = default; ~logger() { - spdlog::drop_all(); + spdlog::shutdown(); } // the following static functions can be used to interact diff --git a/src/common/net/CMakeLists.txt b/src/common/net/CMakeLists.txt index e38e6731..68c5658e 100644 --- a/src/common/net/CMakeLists.txt +++ b/src/common/net/CMakeLists.txt @@ -29,7 +29,7 @@ target_sources( PRIVATE endpoint.cpp client.cpp ) -target_link_libraries(_rpc_client PUBLIC common::config common::logger thallium) +target_link_libraries(_rpc_client PUBLIC common::logger thallium) set_property(TARGET _rpc_client PROPERTY POSITION_INDEPENDENT_CODE ON) add_library(_rpc_server STATIC) @@ -39,4 +39,4 @@ target_sources( PRIVATE server.cpp ) -target_link_libraries(_rpc_server PUBLIC common::config common::logger thallium) +target_link_libraries(_rpc_server PUBLIC common::logger thallium) diff --git a/src/common/net/server.cpp b/src/common/net/server.cpp index 533ba698..616dc90c 100644 --- a/src/common/net/server.cpp +++ b/src/common/net/server.cpp @@ -37,7 +37,6 @@ #include #endif -#include #include #include #include "server.hpp" @@ -46,6 +45,15 @@ using namespace std::literals; namespace scord::network { +server::server(std::string name, std::string address, bool daemonize, + fs::path rundir) + : m_name(std::move(name)), m_address(std::move(address)), + m_daemonize(daemonize), m_rundir(std::move(rundir)), + m_pidfile(daemonize ? std::make_optional(m_rundir / (m_name + ".pid")) + : std::nullopt), + m_logger_config(m_name, scord::logger_type::console_color), + m_network_engine(m_address, THALLIUM_SERVER_MODE) {} + server::~server() = default; pid_t @@ -63,6 +71,7 @@ server::daemonize() { * Manage signals */ + using fork_event = scord::utils::signal_listener::fork_event; pid_t pid, sid; /* Check if this is already a daemon */ @@ -81,6 +90,7 @@ server::daemonize() { logger::destroy_global_logger(); /* Fork off the parent process */ + m_signal_listener.notify_fork(fork_event::fork_prepare); pid = fork(); // re-initialize logging facilities (post-fork) @@ -93,9 +103,12 @@ server::daemonize() { /* Parent returns to caller */ if(pid != 0) { + m_signal_listener.notify_fork(fork_event::fork_parent); return pid; } + m_signal_listener.notify_fork(fork_event::fork_child); + /* Become a session and process group leader with no controlling tty */ if((sid = setsid()) < 0) { /* Log failure */ @@ -142,7 +155,12 @@ server::daemonize() { */ int pfd; - if((pfd = ::open(m_settings.pidfile().c_str(), O_RDWR | O_CREAT, 0640)) == + if(!m_pidfile.has_value()) { + LOGGER_ERROR("Daemon lock file not specified"); + exit(EXIT_FAILURE); + } + + if((pfd = ::open(m_pidfile->string().c_str(), O_RDWR | O_CREAT, 0640)) == -1) { LOGGER_ERRNO("Failed to create daemon lock file"); exit(EXIT_FAILURE); @@ -177,11 +195,6 @@ server::daemonize() { return 0; } -config::settings -server::get_configuration() const { - return m_settings; -} - void server::signal_handler(int signum) { @@ -199,6 +212,7 @@ server::signal_handler(int signum) { case SIGHUP: LOGGER_WARN("A signal (SIGHUP) occurred."); + logger::get_global_logger()->flush(); break; default: @@ -209,29 +223,28 @@ server::signal_handler(int signum) { void server::init_logger() { - if(m_settings.use_console()) { - logger::create_global_logger(m_settings.progname(), "console color"); - return; - } - - if(m_settings.use_syslog()) { - logger::create_global_logger(m_settings.progname(), "syslog"); - - if(!m_settings.daemonize()) { - fmt::print(stderr, "PSA: Output sent to syslog while in " - "non-daemon mode\n"); - } - - return; - } - - if(!m_settings.log_file().empty()) { - logger::create_global_logger(m_settings.progname(), "file", - m_settings.log_file()); - return; + switch(m_logger_config.type()) { + case console_color: + logger::create_global_logger(m_logger_config.ident(), + logger_type::console_color); + break; + case syslog: + logger::create_global_logger(m_logger_config.ident(), + logger_type::syslog); + break; + case file: + if(m_logger_config.log_file().has_value()) { + logger::create_global_logger(m_logger_config.ident(), + logger_type::file, + *m_logger_config.log_file()); + break; + } + [[fallthrough]]; + case console: + logger::create_global_logger(m_logger_config.ident(), + logger_type::console); + break; } - - logger::create_global_logger(m_settings.progname(), "console color"); } void @@ -253,8 +266,8 @@ server::check_configuration() {} void server::print_greeting() { - const auto greeting = fmt::format("Starting {} daemon (pid {})", - m_settings.progname(), getpid()); + const auto greeting = + fmt::format("Starting {} daemon (pid {})", m_name, getpid()); LOGGER_INFO("{:=>{}}", "", greeting.size()); LOGGER_INFO(greeting); @@ -265,27 +278,24 @@ void server::print_configuration() { LOGGER_INFO(""); LOGGER_INFO("[[ Configuration ]]"); - LOGGER_INFO(" - running as daemon?: {}", - (m_settings.daemonize() ? "yes" : "no")); - - if(!m_settings.log_file().empty()) { - LOGGER_INFO(" - log file: {}", m_settings.log_file()); - LOGGER_INFO(" - log file maximum size: {}", - m_settings.log_file_max_size()); - } else { - LOGGER_INFO(" - log file: none"); + LOGGER_INFO(" - running as daemon?: {}", m_daemonize ? "yes" : "no"); + + if(m_logger_config.log_file().has_value()) { + LOGGER_INFO(" - log file: {}", *m_logger_config.log_file()); + } + + if(m_pidfile.has_value()) { + LOGGER_INFO(" - pidfile: {}", *m_pidfile); } - LOGGER_INFO(" - pidfile: {}", m_settings.pidfile()); - LOGGER_INFO(" - port for remote requests: {}", m_settings.remote_port()); - LOGGER_INFO(" - workers: {}", m_settings.workers_in_pool()); + LOGGER_INFO(" - address for remote requests: {}", m_address); LOGGER_INFO(""); } void server::print_farewell() { - const auto farewell = fmt::format("Stopping {} daemon (pid {})", - m_settings.progname(), getpid()); + const auto farewell = + fmt::format("Stopping {} daemon (pid {})", m_name, getpid()); LOGGER_INFO("{:=>{}}", "", farewell.size()); LOGGER_INFO(farewell); @@ -309,9 +319,9 @@ server::run() { #endif // daemonize if needed - if(m_settings.daemonize() && daemonize() != 0) { - /* parent clean ups and exits, child continues */ - teardown(); + if(m_daemonize && daemonize() != 0) { + /* parent exits, child continues */ + shutdown(); return EXIT_SUCCESS; } @@ -345,11 +355,16 @@ server::teardown() { LOGGER_INFO("* Stopping signal listener..."); m_signal_listener.stop(); + if(!m_daemonize || !m_pidfile) { + return; + } + + LOGGER_INFO("* Removing pidfile..."); std::error_code ec; - fs::remove(m_settings.pidfile(), ec); + fs::remove(*m_pidfile, ec); if(ec) { - LOGGER_ERROR("Failed to remove pidfile {}: {}", m_settings.pidfile(), + LOGGER_ERROR("Failed to remove pidfile {}: {}", *m_pidfile, ec.message()); } } diff --git a/src/common/net/server.hpp b/src/common/net/server.hpp index 05f6d708..a36a07b4 100644 --- a/src/common/net/server.hpp +++ b/src/common/net/server.hpp @@ -25,7 +25,9 @@ #ifndef SCORD_SERVER_HPP #define SCORD_SERVER_HPP -#include +#include +#include +#include #include #include #include @@ -38,27 +40,18 @@ using request = thallium::request; class server { public: - template - explicit server(config::settings cfg, Handlers&&... handlers) - : m_settings(std::move(cfg)) { + server(std::string name, std::string address, bool daemonize, + fs::path rundir); - using namespace std::literals; - - const std::string thallim_address = - m_settings.transport_protocol() + "://"s + - m_settings.bind_address() + ":"s + - std::to_string(m_settings.remote_port()); - - m_network_engine = - thallium::engine(thallim_address, THALLIUM_SERVER_MODE); + ~server(); - (set_handler(std::forward(handlers)), ...); + template + void + configure_logger(scord::logger_type type, Args&&... args) { + m_logger_config = + logger_config(m_name, type, std::forward(args)...); } - ~server(); - - config::settings - get_configuration() const; int run(); void @@ -95,7 +88,12 @@ private: print_farewell(); private: - scord::config::settings m_settings; + std::string m_name; + std::string m_address; + bool m_daemonize; + fs::path m_rundir; + std::optional m_pidfile; + logger_config m_logger_config; thallium::engine m_network_engine; scord::utils::signal_listener m_signal_listener; }; diff --git a/src/common/utils/signal_listener.hpp b/src/common/utils/signal_listener.hpp index 44fa7261..f47eff0e 100644 --- a/src/common/utils/signal_listener.hpp +++ b/src/common/utils/signal_listener.hpp @@ -42,6 +42,8 @@ namespace ba = boost::asio; struct signal_listener { + using fork_event = ba::execution_context::fork_event; + using SignalHandlerType = std::function; signal_listener() : m_ios(), m_signals(m_ios) {} @@ -80,6 +82,11 @@ struct signal_listener { }).detach(); } + void + notify_fork(signal_listener::fork_event event) { + m_ios.notify_fork(event); + } + void stop() { m_ios.stop(); diff --git a/src/lib/libscord.cpp b/src/lib/libscord.cpp index a73c7738..ebc0c206 100644 --- a/src/lib/libscord.cpp +++ b/src/lib/libscord.cpp @@ -53,11 +53,11 @@ init_logger() { if(const auto p = std::getenv(scord::env::LOG); p && !std::string{p}.empty() && std::string{p} != "0") { if(const auto log_file = std::getenv(scord::env::LOG_OUTPUT)) { - scord::logger::create_global_logger("libscord", "file", - log_file); + scord::logger::create_global_logger( + "libscord", scord::logger_type::file, log_file); } else { - scord::logger::create_global_logger("libscord", - "console color"); + scord::logger::create_global_logger( + "libscord", scord::logger_type::console_color); } } } catch(const std::exception& ex) { diff --git a/src/scord-ctl/CMakeLists.txt b/src/scord-ctl/CMakeLists.txt index 58ef5805..c3663918 100644 --- a/src/scord-ctl/CMakeLists.txt +++ b/src/scord-ctl/CMakeLists.txt @@ -26,7 +26,7 @@ add_executable(scord-ctl) target_sources( - scord-ctl PRIVATE scord-ctl.cpp rpc_handlers.hpp rpc_handlers.cpp env.hpp + scord-ctl PRIVATE scord-ctl.cpp rpc_handlers.hpp rpc_handlers.cpp ) target_include_directories( @@ -36,8 +36,8 @@ target_include_directories( ) target_link_libraries( - scord-ctl PRIVATE common::config common::logger common::network::rpc_server - libscord_cxx_types fmt::fmt Boost::program_options + scord-ctl PRIVATE common::logger common::network::rpc_server + libscord_cxx_types fmt::fmt CLI11::CLI11 ) install(TARGETS scord DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/scord-ctl/env.hpp b/src/scord-ctl/env.hpp deleted file mode 100644 index 920199cf..00000000 --- a/src/scord-ctl/env.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/****************************************************************************** - * Copyright 2021-2022, Barcelona Supercomputing Center (BSC), Spain - * - * This software was partially supported by the EuroHPC-funded project ADMIRE - * (Project ID: 956748, https://www.admire-eurohpc.eu). - * - * This file is part of scord. - * - * scord is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * scord is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with scord. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - *****************************************************************************/ - -#ifndef SCORD_CTL_ENV_HPP -#define SCORD_CTL_ENV_HPP - -#define SCORD_ENV_PREFIX "SCORDCTL_" -#define ADD_PREFIX(str) SCORD_ENV_PREFIX str - -namespace scord_ctl::env { - -static constexpr auto LOG = ADD_PREFIX("LOG"); -static constexpr auto LOG_OUTPUT = ADD_PREFIX("LOG_OUTPUT"); - -} // namespace scord_ctl::env - - -#endif // SCORD_CTL_ENV_HPP diff --git a/src/scord-ctl/scord-ctl.cpp b/src/scord-ctl/scord-ctl.cpp index d616dabf..ab2d1e0f 100644 --- a/src/scord-ctl/scord-ctl.cpp +++ b/src/scord-ctl/scord-ctl.cpp @@ -23,175 +23,80 @@ *****************************************************************************/ #include -#include #include -#include #include #include #include #include +#include #include #include -#include #include "rpc_handlers.hpp" -#include "env.hpp" namespace fs = std::filesystem; -namespace bpo = boost::program_options; using namespace std::literals; -void -print_version(const std::string& progname) { - fmt::print("{} {}\n", progname, scord::version_string); -} - -void -print_help(const std::string& progname, - const bpo::options_description& opt_desc) { - fmt::print("Usage: {} [options]\n\n", progname); - fmt::print("{}", opt_desc); -} - -std::unordered_map -load_envs() { - - std::unordered_map envs; - - if(const auto p = std::getenv(scord_ctl::env::LOG); - p && !std::string{p}.empty() && std::string{p} != "0") { - if(const auto log_file = std::getenv(scord_ctl::env::LOG_OUTPUT)) { - envs.emplace(scord_ctl::env::LOG_OUTPUT, log_file); - } - } - - return envs; -} int main(int argc, char* argv[]) { - scord::config::settings cfg; - - // define the command line options allowed - bpo::options_description opt_desc("Options"); - opt_desc.add_options() - // run in foreground - (",f", - bpo::bool_switch()->default_value(false)->notifier( - [&](const bool& flag_value) { - cfg.daemonize(!flag_value); - }), - "foreground operation") - - // force logging messages to the console - ("force-console,C", - bpo::value() - ->implicit_value("") - ->zero_tokens() - ->notifier([&](const std::string&) { - cfg.log_file(fs::path{}); - cfg.use_console(true); - }), - "override any logging options defined in configuration files and " - "send all daemon output to the console") - - // use provided configuration file instead of the system-wide - // configuration file defined when building the daemon - ("config-file,c", - bpo::value() - ->value_name("FILENAME") - ->implicit_value("") - ->notifier([&](const std::string& filename) { - cfg.config_file(filename); - }), - "override the system-wide configuration file with FILENAME") - - // print the daemon version - ("version,v", - bpo::value()->implicit_value("")->zero_tokens(), - "print version string") - - // print help - ("help,h", - bpo::value()->implicit_value("")->zero_tokens(), - "produce help message"); - - // parse the command line - bpo::variables_map vm; - - try { - bpo::store(bpo::parse_command_line(argc, argv, opt_desc), vm); + struct { + std::optional output_file; + std::string address; + } cli_args; - // the --help and --version arguments are special, since we want - // to process them even if the global configuration file doesn't exist - if(vm.count("help")) { - print_help(cfg.progname(), opt_desc); - return EXIT_SUCCESS; - } + const auto progname = fs::path{argv[0]}.filename().string(); - if(vm.count("version")) { - print_version(cfg.progname()); - return EXIT_SUCCESS; - } + CLI::App app{"", progname}; - const fs::path config_file = (vm.count("config-file") == 0) - ? cfg.config_file() - : vm["config-file"].as(); + // force logging messages to file + app.add_option("-o,--output", cli_args.output_file, + "Write any output to FILENAME rather than sending it to the " + "console") + ->option_text("FILENAME"); - if(!fs::exists(config_file)) { - fmt::print(stderr, - "Failed to access daemon configuration file {}\n", - config_file); - return EXIT_FAILURE; - } + app.add_option("-l,--listen", cli_args.address, + "Address or interface to bind the daemon to. If using " + "`libfabric`,\n" + "the address is typically in the form of:\n\n" + " ofi+[://:]\n\n" + "Check `fi_info` to see the list of available protocols.\n") + ->option_text("ADDRESS") + ->required(); - try { - cfg.load_from_file(config_file); - } catch(const std::exception& ex) { - fmt::print(stderr, - "Failed reading daemon configuration file:\n" - " {}\n", - ex.what()); - return EXIT_FAILURE; - } + app.add_flag_function( + "-v,--version", + [&](auto /*count*/) { + fmt::print("{} {}\n", progname, scord::version_string); + std::exit(EXIT_SUCCESS); + }, + "Print version and exit"); - // override settings from the configuration file with settings - // from environment variables - const auto env_opts = load_envs(); - - if(const auto& it = env_opts.find(scord_ctl::env::LOG_OUTPUT); - it != env_opts.end()) { - cfg.log_file(it->second); - } - - // calling notify() here basically invokes all define notifiers, thus - // overriding any configuration loaded from the global configuration - // file with its command-line counterparts if provided (for those - // options where this is available) - bpo::notify(vm); - } catch(const bpo::error& ex) { - fmt::print(stderr, "ERROR: {}\n\n", ex.what()); - return EXIT_FAILURE; - } + CLI11_PARSE(app, argc, argv); try { - scord::network::server daemon(cfg); + scord::network::server srv(progname, cli_args.address, false, + fs::current_path()); + if(cli_args.output_file) { + srv.configure_logger(scord::logger_type::file, + *cli_args.output_file); + } // convenience macro to ensure the names of an RPC and its handler // always match #define EXPAND(rpc_name) "ADM_" #rpc_name##s, scord::network::handlers::rpc_name - daemon.set_handler(EXPAND(ping)); + srv.set_handler(EXPAND(ping)); #undef EXPAND - return daemon.run(); + return srv.run(); } catch(const std::exception& ex) { fmt::print(stderr, "An unhandled exception reached the top of main(), " "{} will exit:\n what(): {}\n", - cfg.progname(), ex.what()); + progname, ex.what()); return EXIT_FAILURE; } } diff --git a/src/scord/CMakeLists.txt b/src/scord/CMakeLists.txt index ec92c395..eb91e16c 100644 --- a/src/scord/CMakeLists.txt +++ b/src/scord/CMakeLists.txt @@ -25,8 +25,11 @@ # scord daemon add_executable(scord) -target_sources(scord PRIVATE scord.cpp rpc_handlers.hpp rpc_handlers.cpp - env.hpp job_manager.hpp adhoc_storage_manager.hpp pfs_storage_manager.hpp) +target_sources(scord PRIVATE scord.cpp rpc_handlers.hpp + rpc_handlers.cpp job_manager.hpp adhoc_storage_manager.hpp + pfs_storage_manager.hpp ${CMAKE_BINARY_DIR}/defaults.hpp) + +configure_file(defaults.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/defaults.hpp @ONLY) target_include_directories( scord @@ -36,16 +39,17 @@ target_include_directories( target_link_libraries( scord - PRIVATE common::config - common::logger - common::network::rpc_server - common::utils - common::abt_cxx - libscord_cxx_types - tl::expected - fmt::fmt - Boost::program_options - RedisPlusPlus::RedisPlusPlus + PRIVATE + common::logger + common::network::rpc_server + common::utils + common::abt_cxx + libscord_cxx_types + tl::expected + fmt::fmt + CLI11::CLI11 + RedisPlusPlus::RedisPlusPlus + ryml::ryml ) install(TARGETS scord DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/scord/env.hpp b/src/scord/defaults.hpp.in similarity index 71% rename from src/scord/env.hpp rename to src/scord/defaults.hpp.in index 6d73fbfc..eb31b596 100644 --- a/src/scord/env.hpp +++ b/src/scord/defaults.hpp.in @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright 2021-2022, Barcelona Supercomputing Center (BSC), Spain + * Copyright 2021-2023, Barcelona Supercomputing Center (BSC), Spain * * This software was partially supported by the EuroHPC-funded project ADMIRE * (Project ID: 956748, https://www.admire-eurohpc.eu). @@ -22,18 +22,18 @@ * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ -#ifndef SCORD_ENV_HPP -#define SCORD_ENV_HPP -#define SCORD_ENV_PREFIX "SCORD_" -#define ADD_PREFIX(str) SCORD_ENV_PREFIX str +#ifndef SCORD_DEFAULTS_HPP +#define SCORD_DEFAULTS_HPP -namespace scord::env { +#include -static constexpr auto LOG = ADD_PREFIX("LOG"); -static constexpr auto LOG_OUTPUT = ADD_PREFIX("LOG_OUTPUT"); +namespace scord::config::defaults { -} // namespace scord::env +static constexpr bool daemonize{true}; +static const std::filesystem::path config_file{ + "@CMAKE_INSTALL_FULL_SYSCONFDIR@/@CMAKE_PROJECT_NAME@.conf"}; +} // namespace scord::config::defaults -#endif // SCORD_ENV_HPP +#endif // SCORD_DEFAULTS_HPP diff --git a/src/scord/scord.cpp b/src/scord/scord.cpp index bdace7a7..86001af9 100644 --- a/src/scord/scord.cpp +++ b/src/scord/scord.cpp @@ -23,189 +23,190 @@ *****************************************************************************/ #include -#include #include -#include #include #include #include #include +#include #include #include -#include #include "rpc_handlers.hpp" -#include "env.hpp" +#include "defaults.hpp" #include +#include +#include namespace fs = std::filesystem; -namespace bpo = boost::program_options; using namespace std::literals; -void -print_version(const std::string& progname) { - fmt::print("{} {}\n", progname, scord::version_string); -} - -void -print_help(const std::string& progname, - const bpo::options_description& opt_desc) { - fmt::print("Usage: {} [options]\n\n", progname); - fmt::print("{}", opt_desc); -} - -std::unordered_map -load_envs() { - - std::unordered_map envs; - - if(const auto p = std::getenv(scord::env::LOG); - p && !std::string{p}.empty() && std::string{p} != "0") { - if(const auto log_file = std::getenv(scord::env::LOG_OUTPUT)) { - envs.emplace(scord::env::LOG_OUTPUT, log_file); - } +class config_yaml : public CLI::Config { +public: + std::string + to_config(const CLI::App*, bool, bool, std::string) const final { + return {}; } - return envs; -} - -int -main(int argc, char* argv[]) { - - scord::config::settings cfg; - - // define the command line options allowed - bpo::options_description opt_desc("Options"); - opt_desc.add_options() - // run in foreground - (",f", - bpo::bool_switch()->default_value(false)->notifier( - [&](const bool& flag_value) { - cfg.daemonize(!flag_value); - }), - "foreground operation") - - // force logging messages to the console - ("force-console,C", - bpo::value() - ->implicit_value("") - ->zero_tokens() - ->notifier([&](const std::string&) { - cfg.log_file(fs::path{}); - cfg.use_console(true); - }), - "override any logging options defined in configuration files and " - "send all daemon output to the console") - - // use provided configuration file instead of the system-wide - // configuration file defined when building the daemon - ("config-file,c", - bpo::value() - ->value_name("FILENAME") - ->implicit_value("") - ->notifier([&](const std::string& filename) { - cfg.config_file(filename); - }), - "override the system-wide configuration file with FILENAME") - - // print the daemon version - ("version,v", - bpo::value()->implicit_value("")->zero_tokens(), - "print version string") - - // print help - ("help,h", - bpo::value()->implicit_value("")->zero_tokens(), - "produce help message"); - - // parse the command line - bpo::variables_map vm; - - try { - bpo::store(bpo::parse_command_line(argc, argv, opt_desc), vm); - - // the --help and --version arguments are special, since we want - // to process them even if the global configuration file doesn't exist - if(vm.count("help")) { - print_help(cfg.progname(), opt_desc); - return EXIT_SUCCESS; + std::vector + parse_node(const ryml::ConstNodeRef& node, + const std::string& parent_name = "", + const std::vector& prefix = {}) const { + std::vector results; + + if(node.is_map()) { + for(const auto& c : node.children()) { + auto copy_prefix = prefix; + if(!parent_name.empty()) { + copy_prefix.push_back(parent_name); + } + + std::string name; + if(c.has_key()) { + ryml::from_chars(c.key(), &name); + } + + const auto sub_results = parse_node(c, name, copy_prefix); + results.insert(results.end(), sub_results.begin(), + sub_results.end()); + } + return results; } - if(vm.count("version")) { - print_version(cfg.progname()); - return EXIT_SUCCESS; + if(!parent_name.empty()) { + auto& res = results.emplace_back(); + res.name = parent_name; + res.parents = prefix; + + if(node.is_seq()) { + for(const auto& c : node.children()) { + if(c.has_val()) { + std::string value{c.val().data(), c.val().size()}; + res.inputs.push_back(value); + } + } + return results; + } + + if(node.is_keyval()) { + std::string value{node.val().data(), node.val().size()}; + res.inputs = {value}; + return results; + } } - const fs::path config_file = (vm.count("config-file") == 0) - ? cfg.config_file() - : vm["config-file"].as(); - - if(!fs::exists(config_file)) { - fmt::print(stderr, - "Failed to access daemon configuration file {}\n", - config_file); - return EXIT_FAILURE; - } + throw CLI::ConversionError("Missing name"); + } - try { - cfg.load_from_file(config_file); - } catch(const std::exception& ex) { - fmt::print(stderr, - "Failed reading daemon configuration file:\n" - " {}\n", - ex.what()); - return EXIT_FAILURE; - } + std::vector + from_config(std::istream& input) const final { + std::string input_str{std::istreambuf_iterator(input), + std::istreambuf_iterator()}; + ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(input_str)); + return parse_node(tree.crootref()); + } +}; - // override settings from the configuration file with settings - // from environment variables - const auto env_opts = load_envs(); - if(const auto& it = env_opts.find(scord::env::LOG_OUTPUT); - it != env_opts.end()) { - cfg.log_file(it->second); - } +int +main(int argc, char* argv[]) { - // calling notify() here basically invokes all define notifiers, thus - // overriding any configuration loaded from the global configuration - // file with its command-line counterparts if provided (for those - // options where this is available) - bpo::notify(vm); - } catch(const bpo::error& ex) { - fmt::print(stderr, "ERROR: {}\n\n", ex.what()); + struct { + bool foreground = !scord::config::defaults::daemonize; + scord::logger_type log_type = scord::logger_type::console_color; + std::optional output_file; + std::optional rundir; + std::optional address; + } cli_args; + + const auto progname = fs::path{argv[0]}.filename().string(); + + CLI::App app{"scord - An orchestrator for HPC I/O activity", progname}; + app.config_formatter(std::make_shared()); + + app.add_flag("-f,--foreground", cli_args.foreground, "Run in foreground"); + app.add_flag_callback( + "-C,--force-console", + [&]() { + cli_args.log_type = scord::logger_type::console_color; + cli_args.output_file = std::nullopt; + }, + "Override any logging options defined in the configuration file " + "and send all daemon output to the console"); + + app.set_config("-c,--config-file", scord::config::defaults::config_file, + "Ignore the system-wide configuration file and use the " + "configuration provided by FILENAME", + /*config_required=*/true) + ->option_text("FILENAME") + ->check(CLI::ExistingFile); + + app.add_flag_callback( + "-v,--version", + [&]() { + fmt::print("{} {}\n", progname, scord::version_string); + std::exit(EXIT_SUCCESS); + }, + "Print version string and exit"); + + // options accepted by configuration file + auto global_settings = app.add_subcommand("global_settings") + ->configurable(true) + ->group(""); + global_settings->add_option_function( + "--logfile", [&](const std::string& val) { + cli_args.log_type = scord::logger_type::file; + cli_args.output_file = fs::path{val}; + }); + global_settings->add_option("--rundir", cli_args.rundir); + global_settings->add_option("--address", cli_args.address); + + CLI11_PARSE(app, argc, argv); + + // ->required(true) doesn't work with configurable subcommands + if(!cli_args.address) { + fmt::print(stderr, + "{}: error: required option 'address' missing " + "from configuration file\n", + progname); return EXIT_FAILURE; } try { - scord::network::server daemon(cfg); + scord::network::server srv( + progname, *cli_args.address, !cli_args.foreground, + cli_args.rundir.value_or(fs::current_path())); + + srv.configure_logger(cli_args.log_type, cli_args.output_file); - // convenience macro to ensure the names of an RPC and its handler - // always match + // convenience macro to ensure the names of an RPC and + // its handler always match #define EXPAND(rpc_name) "ADM_" #rpc_name##s, scord::network::handlers::rpc_name - daemon.set_handler(EXPAND(ping)); - daemon.set_handler(EXPAND(register_job)); - daemon.set_handler(EXPAND(update_job)); - daemon.set_handler(EXPAND(remove_job)); - daemon.set_handler(EXPAND(register_adhoc_storage)); - daemon.set_handler(EXPAND(update_adhoc_storage)); - daemon.set_handler(EXPAND(remove_adhoc_storage)); - daemon.set_handler(EXPAND(deploy_adhoc_storage)); - daemon.set_handler(EXPAND(tear_down_adhoc_storage)); - daemon.set_handler(EXPAND(register_pfs_storage)); - daemon.set_handler(EXPAND(update_pfs_storage)); - daemon.set_handler(EXPAND(remove_pfs_storage)); - daemon.set_handler(EXPAND(transfer_datasets)); + srv.set_handler(EXPAND(ping)); + srv.set_handler(EXPAND(register_job)); + srv.set_handler(EXPAND(update_job)); + srv.set_handler(EXPAND(remove_job)); + srv.set_handler(EXPAND(register_adhoc_storage)); + srv.set_handler(EXPAND(update_adhoc_storage)); + srv.set_handler(EXPAND(remove_adhoc_storage)); + srv.set_handler(EXPAND(deploy_adhoc_storage)); + srv.set_handler(EXPAND(tear_down_adhoc_storage)); + srv.set_handler(EXPAND(register_pfs_storage)); + srv.set_handler(EXPAND(update_pfs_storage)); + srv.set_handler(EXPAND(remove_pfs_storage)); + srv.set_handler(EXPAND(transfer_datasets)); #undef EXPAND - return daemon.run(); + return srv.run(); } catch(const std::exception& ex) { fmt::print(stderr, - "An unhandled exception reached the top of main(), " - "{} will exit:\n what(): {}\n", - cfg.progname(), ex.what()); + "{}: error: an unhandled exception reached the top " + "of main(), {} will exit:\n" + " what(): {}\n", + progname, progname, ex.what()); return EXIT_FAILURE; } } -- GitLab