Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • cellframe/cellframe-node
  • evseev/cellframe-node
  • dmitry.puzyrkov/cellframe-node
  • MIKA83/cellframe-node
4 results
Show changes
Showing
with 1425 additions and 0 deletions
cmake_minimum_required(VERSION 3.10)
include(FetchContent)
project(cellframe-node-config)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
SET (CONFTOOL_SRC ./main.cpp
./commands/AbstractCommand.cpp
./commands/NetworkCommand.cpp
./commands/ConditionCommand.cpp
./commands/StorageCommand.cpp
./commands/ConfigCommand.cpp
./commands/ServiceCommand.cpp
./commands/FromTemplateCommand.cpp
./commands/NetworkListCommand.cpp
./config/CellframeConfigFile.cpp
./service/service_win.cpp
./service/service_linux.cpp)
if (ANDROID)
add_library(${PROJECT_NAME} SHARED ${CONFTOOL_SRC})
else ()
add_executable(${PROJECT_NAME} ${CONFTOOL_SRC})
endif ()
if (LINUX OR WIN32 )
target_link_libraries(cellframe-node-config PRIVATE stdc++fs )
endif()
if (APPLE)
target_link_libraries(cellframe-node-config PUBLIC "-framework Security -framework Foundation")
add_library(conftool_osx_ctrl STATIC ./service/service_macos.cpp ./service/macos_auth.m)
set_target_properties(conftool_osx_ctrl PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(cellframe-node-config PUBLIC conftool_osx_ctrl)
endif()
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
#if (ANDROID)
# add_definitions("-DANDROID_STL=c++_static")
#endif()
IF (WIN32)
target_link_libraries(cellframe-node-config PRIVATE ws2_32)
ENDIF()
#ifdef __linux__
#define HOST_OS "Linux"
#endif
#ifdef WIN32
#define HOST_OS "Windows"
#endif
#ifdef __APPLE__
#define HOST_OS "Macos"
#endif
\ No newline at end of file
//
// Created by dpuzyrkov on 9/17/24.
//
#ifndef NODE_CELLFRAME_NODE_CONFIG_H
#define NODE_CELLFRAME_NODE_CONFIG_H
#include "commands/AbstractCommand.h"
namespace conftool {
void populate_variables(std::string basepath);//must be specificly called befor init_configs
int init_configs(std::string init_file_name, int flags, int non_interactive);
std::unique_ptr<CAbstractScriptCommand> parse_line_to_cmd(std::string line, int line_no, int flags);
bool run_commands(std::vector <std::unique_ptr<CAbstractScriptCommand>> &commands, int interactive, int flags);
}
#endif //NODE_CELLFRAME_NODE_CONFIG_H
#include "AbstractCommand.h"
#include "NetworkCommand.h"
#include <string>
std::vector<std::string> tokenize(const std::string &str, const std::regex re)
{
std::regex_token_iterator it{ str.begin(), str.end(), re, -1 };
std::vector<std::string> tokenized{ it, {} };
tokenized.erase(
std::remove_if(tokenized.begin(),
tokenized.end(),
[](std::string const& s) {
return s.size() == 0;
}),
tokenized.end());
return tokenized;
}
std::string string_join(const std::vector<std::string>& vec, const char* delim)
{
std::stringstream res;
copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(res, delim));
return res.str();
}
CAbstractScriptCommand::CAbstractScriptCommand(std::vector<std::string> tokens)
{
this->cmd_tokens = tokens;
}
std::unordered_map<std::string, CAbstractScriptCommand::create_f *> & CAbstractScriptCommand::registry()
{
static std::unordered_map<std::string, CAbstractScriptCommand::create_f *> impl;
return impl;
}
std::unique_ptr<CAbstractScriptCommand> CAbstractScriptCommand::build(const std::string &line)
{
auto tokens = tokenize(line, std::regex("\\s+"));
auto cmd = tokens[0];
if (!CAbstractScriptCommand::exists(cmd)) return NULL;
return CAbstractScriptCommand::instantiate(cmd, tokens);
}
std::string CAbstractScriptCommand::represent()
{
return string_join(this->cmd_tokens, " ");
}
bool CAbstractScriptCommand::is_condition_open(){
return false;
}
bool CAbstractScriptCommand::is_condition_close(){
return false;
}
std::map<std::string, std::string> variable_storage;
\ No newline at end of file
#pragma once
#include <algorithm>
#include <string>
#include <iostream>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <vector>
#include <regex>
#include <unordered_map>
#include <memory>
#include <string>
#include <stdexcept>
#include <sstream>
#include <iostream>
#include <iterator>
#include <numeric>
#include <map>
#include "../build_config.h"
enum E_FLAGS {
F_VERBOSE = 1 << 0,
F_FORCE = 1 << 1,
F_DRYRUN = 1 << 2
};
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf( new char[ size ] );
std::snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
// trim from start (in place)
inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
inline void trim(std::string &s) {
rtrim(s);
ltrim(s);
}
std::string string_join(const std::vector<std::string>& vec, const char* delim);
std::vector<std::string> tokenize(const std::string &str, const std::regex re);
struct CAbstractScriptCommand {
CAbstractScriptCommand(std::vector<std::string> tokens);
virtual ~CAbstractScriptCommand() = default;
using create_f = std::unique_ptr<CAbstractScriptCommand>(std::vector <std::string> cmd_tokens);
static void registrate(std::string const & name, create_f * fp)
{
registry()[name] = fp;
}
static bool exists(std::string const & name)
{
auto it = registry().find(name);
return it != registry().end();
}
static std::unique_ptr<CAbstractScriptCommand> instantiate(std::string const & name, std::vector <std::string> cmd_tokens)
{
auto it = registry().find(name);
return it == registry().end() ? nullptr : (it->second)(cmd_tokens);
}
template <typename D>
struct Registrar
{
explicit Registrar(std::string const & name)
{
CAbstractScriptCommand::registrate(name, &D::create);
}
// make non-copyable, etc.
};
private:
static std::unordered_map<std::string, create_f *> & registry();
CAbstractScriptCommand();
std::vector<std::string> cmd_tokens;
public:
virtual bool execute(bool non_interactive, int flags) = 0;
static std::unique_ptr<CAbstractScriptCommand> build(const std::string &line);
virtual bool is_condition_open();
virtual bool is_condition_close();
virtual std::string represent();
};
extern std::map<std::string, std::string> variable_storage;
\ No newline at end of file
#include "ConditionCommand.h"
#include <sstream>
#include <iostream>
#include <iterator>
#include <numeric>
#include "../config/CellframeConfigFile.h"
CAbstractScriptCommand::Registrar<CConditionOpenCommand> if_registrar("if");
CAbstractScriptCommand::Registrar<CConditionCloseCommand> endif_registrar("endif");
bool CConditionOpenCommand::is_condition_open() {return true;}
bool CConditionCloseCommand::is_condition_close() {return true;}
CConditionOpenCommand::CConditionOpenCommand(std::vector <std::string> cmd_tokens):CAbstractScriptCommand(cmd_tokens)
{
std::string joined_condition = string_join(std::vector<std::string>(cmd_tokens.begin()+1,cmd_tokens.end()), "");
auto eq_token = joined_condition.find("==");
auto neq_token = joined_condition.find("!=");
if (eq_token != std::string::npos)
this->cond_op = COND_EQUAL;
if (neq_token != std::string::npos)
this->cond_op = COND_NOT_EQUAL;
if (eq_token == std::string::npos && neq_token == std::string::npos)
this->cond_op = COND_IS_SET;
std::vector < std::string> tokens = tokenize(joined_condition, std::regex("==|!="));
if (this->cond_op == COND_IS_SET && tokens.size() != 1)
throw std::invalid_argument("IS_SET condition requre only one variable for check");
if (this->cond_op != COND_IS_SET && tokens.size() != 2)
throw std::invalid_argument("CONDITION EQ | NEQ requre only two tokens");
this->arg1 = tokens[0];
if (this->cond_op != COND_IS_SET)
this->arg2 = tokens[1];
//auto neq_token = std::find(joined_condition.begin(), joined_condition.end(), "!=");
}
CConditionCloseCommand::CConditionCloseCommand(std::vector <std::string> cmd_tokens):CAbstractScriptCommand(cmd_tokens)
{
}
bool CConditionOpenCommand::execute(bool non_intercative, int flags)
{
std::string a1val = this->arg1;
std::string a2val = this->arg2;
if (flags & F_VERBOSE) std::cout << "[VE] Condition exec: " << a1val << " " << this->cond_op << " "<<a2val << " -> ";
a1val = substitute_variables(arg1);
a2val = substitute_variables(arg2);
bool res = false;
std::string opsym = "";
switch (this->cond_op)
{
case COND_EQUAL:
{
opsym = "==";
res = a1val == a2val;
break;
}
case COND_NOT_EQUAL:
{
opsym = "!=";
res = a1val != a2val;
break;
}
case COND_IS_SET:
{
opsym = "exists";
res = a1val.empty();
break;
}
default:
throw std::invalid_argument("Unknown condition type");
}
if (flags & F_VERBOSE) std::cout << a1val << " " << opsym << " " <<a2val << " -> " << res << std::endl;
return res;
}
bool CConditionCloseCommand::execute(bool non_intercative, int flags)
{
return true;
}
#pragma once
#include "AbstractCommand.h"
enum E_COND_OPS {
COND_EQUAL,
COND_NOT_EQUAL,
COND_IS_SET
};
class CConditionOpenCommand : public CAbstractScriptCommand {
public:
CConditionOpenCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CConditionOpenCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
bool is_condition_open();
E_COND_OPS cond_op;
std::string arg1;
std::string arg2;
};
class CConditionCloseCommand : public CAbstractScriptCommand {
public:
CConditionCloseCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CConditionCloseCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
bool is_condition_close();
};
\ No newline at end of file
#include "ConfigCommand.h"
#include "../config/CellframeConfigFile.h"
CAbstractScriptCommand::Registrar<CConfigCommand> cfg_registrar("config");
/*
config cellframe-node general auto_online default true
config cellframe-node general debug_mode default false
config cellframe-node server enabled default false
config cellframe-node server listen_address default 127.0.0.1
config cellframe-node server listen_port_tcp default 8079
config cellframe-node notify_server listen_address default 127.0.0.1
config cellframe-node notify_server listen_port default 8080
if MacOS == $HOST_OS
config cellframe-node global_db driver ensure $DB_DRIVER
endif
config Backbone general node-role default full
config KelVPN general node-role default full
config raiden general node-role default full
config riemann general node-role default full
config mileena general node-role default full
config subzero general node-role default full
*/
std::vector<std::string> allowed_actions{"default", "ensure", "get"};
CConfigCommand::CConfigCommand(std::vector <std::string> cmd_tokens):CAbstractScriptCommand(cmd_tokens)
{
//zero token is always a command (config)
if (cmd_tokens.size() <= 4)
throw std::invalid_argument("config command invalid numbers of arguments");
this->cfg_name = cmd_tokens[1];
//check if config name accetable
//net should exist
fs::path net_cfg_template_path = config_path(this->cfg_name, CFG_GENERAL, CFG_TEMPLATE);
if (this->cfg_name != "cellframe-node" && !fs::exists(net_cfg_template_path))
throw std::invalid_argument(string_format("config_cmd: config for [%s] not exitst",
this->cfg_name.c_str()));
this->group = cmd_tokens[2];
this->param = cmd_tokens[3];
this->action = cmd_tokens[4];
if (allowed_actions.end() == std::find(allowed_actions.begin(), allowed_actions.end(), this->action))
{
throw std::invalid_argument(string_format("config_cmd: allowed actions are get|ensure|default, not %s",
this->action.c_str()));
}
if (cmd_tokens.size() > 5)
this->value = cmd_tokens[5];
}
bool is_placeholder(const std::string &val)
{
if (val.empty()) return true;
return val[0]=='$' && val[val.size()-1]=='}';
}
bool CConfigCommand::execute(bool non_intercative, int flags)
{
//can't do this in ctr, cause storage-cmds can be not executed yet.
this->value = substitute_variables(this->value);
auto cfg_on_path = config_path(this->cfg_name, CFG_GENERAL, CFG_ON );
auto cfg_off_path = config_path(this->cfg_name, CFG_GENERAL, CFG_OFF );
fs::path cfg_path = cfg_on_path;
if (this->cfg_name != "cellframe-node")
{
if (fs::exists(cfg_off_path))
cfg_path = cfg_off_path;
if (fs::exists(cfg_on_path))
cfg_path = cfg_on_path;
if (fs::exists(cfg_on_path) && fs::exists(cfg_off_path))
{
std::cout << "[C][config] " << "cfg " << this->cfg_name
<< " is in both on&off states, use enabled cfg for modifications" << std::endl;
}
}
std::map <std::string, std::function<bool ()>> actions;
CellframeConfigurationFile cfg(cfg_path, flags);
//check for default net state
//default means
//set if - no parameter defined
//set if value is placeholder ({CFG})
//if exists - skip
actions["default"] = [*this, &cfg]() {
std::string cfg_val;
bool param_exists = cfg.exists(this->group, this->param, &cfg_val);
if (param_exists && !is_placeholder(cfg_val)){
//skip
std::cout << "[C][config default] " << "[" << this->cfg_name<<"] "
<< "[" << this->group<<"] "
<< this->param << "==" <<cfg_val
<< ", skip altering"<<std::endl;
return false;
}
std::cout << "[C][config default] " << "[" << this->cfg_name<<"]"
<< " [" << this->group<<"] set "
<< this->param << "=" <<this->value
<< std::endl;
cfg.set(this->group, this->param, this->value);
return true;
};
//force net state
actions["ensure"] = [*this, &cfg]() {
std::cout << "[C][config ensure] " << "[" << this->cfg_name<<"] "
<< "[" << this->group<<"] set "
<< this->param << "=" <<this->value
<< std::endl;
cfg.set(this->group, this->param, this->value);
return true;
};
actions["get"] = [*this, &cfg]() {
std::string cfg_val;
bool param_exists = cfg.exists(this->group, this->param, &cfg_val);
std::cout << this->cfg_name << ": ["<<this->group<<"] " << this->param+"="+cfg_val<<std::endl;
return false;
};
//actions return true if config was altered and need to be saved
bool res = actions[this->action]();
if (res) cfg.save();
return true;
}
#pragma once
#include "AbstractCommand.h"
class CConfigCommand : public CAbstractScriptCommand {
public:
CConfigCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CConfigCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
private:
std::string cfg_name;
std::string group;
std::string param;
std::string value;
std::string action;
};
#include "FromTemplateCommand.h"
#include <stdexcept>
#include <filesystem>
#include "../build_config.h"
#include "../config/CellframeConfigFile.h"
namespace fs = std::filesystem;
CAbstractScriptCommand::Registrar<CFromTemplateCommand> tmplt_registrar("fromtemplate");
CFromTemplateCommand::CFromTemplateCommand(std::vector <std::string> cmd_tokens): CAbstractScriptCommand(cmd_tokens)
{
if (cmd_tokens.size() <= 2)
throw std::invalid_argument("config command invalid numbers of arguments");
this->cfg_name = cmd_tokens[1];
//check if config name accetable
fs::path net_cfg_template_path = config_path(this->cfg_name, CFG_GENERAL, CFG_TEMPLATE);
if (this->cfg_name != "cellframe-node" && !fs::exists(net_cfg_template_path))
throw std::invalid_argument("config_cmd: config not exitst");
this->action = cmd_tokens[2];
}
bool CFromTemplateCommand::execute(bool non_intercative, int flags)
{
auto path_from = config_path(this->cfg_name, CFG_GENERAL, CFG_TEMPLATE);
auto path_to = config_path(this->cfg_name, CFG_GENERAL, CFG_ON);
if (this->action == "default")
{
if (fs::exists(path_to))
{
std::cout << "[C][fromtemplate default] " << " [" << this->cfg_name<<"]"
<< " exists, skip altering"<<std::endl;
return true;
}
std::cout <<"[C] [fromtemplate default] "
<<"copy "<< path_from << " to "<< path_to << std::endl;
if (!(flags & F_DRYRUN)) fs::copy_file(path_from, path_to);
}
if (this->action == "ensure")
{
std::cout <<"[C] [fromtemplate ensure] "
<<"copy "<< path_from << " to "<< path_to << std::endl;
if (!(flags & F_DRYRUN)) fs::copy_file(path_from, path_to, fs::copy_options::overwrite_existing);
}
//populate
CellframeConfigurationFile cfg(path_to, flags);
cfg.replace_placeholders(variable_storage);
cfg.save();
return true;
}
\ No newline at end of file
#pragma once
#include "AbstractCommand.h"
class CFromTemplateCommand : public CAbstractScriptCommand {
public:
CFromTemplateCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CFromTemplateCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
private:
std::string cfg_name;
std::string action;
};
#include "NetworkCommand.h"
#include <stdexcept>
#include <filesystem>
#include "../build_config.h"
#include "../config/CellframeConfigFile.h"
namespace fs = std::filesystem;
CAbstractScriptCommand::Registrar<CNetworkCommand> registrar("network");
/*
Network Command syntax:
network {netname} default on
network {netname} default off
network {netname} ensure off
network {netname} ensure on
*/
std::vector<std::string> allowed_states{"on", "off"};
CNetworkCommand::CNetworkCommand(std::vector <std::string> cmd_tokens): CAbstractScriptCommand(cmd_tokens)
{
//zero token is always a command (network)
if (cmd_tokens.size() <= 2)
throw std::invalid_argument("network command has no command arguments");
this->net_name = cmd_tokens[1];
//check such net is available: lookup for config files in share dir
fs::path net_cfg_template_path = config_path(this->net_name, CFG_GENERAL, CFG_TEMPLATE);
if (!fs::exists(net_cfg_template_path))
throw std::invalid_argument("network_cmd: template not found in share config path");
std::map <std::string, std::function<void ()>> actions;
actions["default"] = [this, &cmd_tokens](){
if (cmd_tokens.size() < 4 || allowed_states.end() == std::find(allowed_states.begin(), allowed_states.end(), cmd_tokens[3]))
throw std::invalid_argument("network_cmd: [default] require 'on or off' state for net");
this->default_val = cmd_tokens[3];
};
actions["ensure"] = [this, &cmd_tokens](){
if (cmd_tokens.size() < 4 || allowed_states.end() == std::find(allowed_states.begin(), allowed_states.end(), cmd_tokens[3]))
throw std::invalid_argument("network_cmd: [ensure] require 'on or off' state for net" );
this->default_val = cmd_tokens[3];
};
actions["check"] = [this, &cmd_tokens](){};
this->action = cmd_tokens[2];
if (actions.find(this->action) == actions.end())
throw std::invalid_argument("network_cmd: unknown action for network_cmd");
actions[this->action]();
}
bool CNetworkCommand::execute(bool non_intercative, int flags)
{
std::map <std::string, std::function<bool ()>> actions;
//check for default net state
actions["default"] = [&]() {
//validity of default_value ensured by constructor
fs::path check_exist_path;
fs::path template_copy_dest;
fs::path template_path = config_path(this->net_name, CFG_GENERAL, CFG_TEMPLATE);
if (fs::exists(config_path(this->net_name, CFG_GENERAL, CFG_OFF)) &&
fs::exists(config_path(this->net_name, CFG_GENERAL, CFG_ON)) )
{
std::cout << "[C][network default] you have both enabled and disabled files for network [" << this->net_name << "], skip this step"<<std::endl;
return false;
}
//default requested net on
if (this->default_val == "on")
{
check_exist_path = config_path(this->net_name, CFG_GENERAL, CFG_OFF);
template_copy_dest = config_path(this->net_name, CFG_GENERAL, CFG_ON);
} else if (this->default_val == "off")
{
check_exist_path = config_path(this->net_name, CFG_GENERAL, CFG_ON);
template_copy_dest = config_path(this->net_name, CFG_GENERAL, CFG_OFF);
} else { std::cout << "wtf" << std::endl; return false; }
//skip default if net already exists in any state
if (fs::exists(template_copy_dest)) {
std::cout << "[C][network default " << this->default_val<<"] Network [" << this->net_name << "] already "<< this->default_val << ", skip this step" <<std::endl;
return true;
}
if (fs::exists(check_exist_path))
{
if (flags & F_VERBOSE) std::cout << "[VE][network default] File " << check_exist_path << " exists, but default state requested [" << this->default_val << "]";
std::cout << "[C}[network default] Skip altering state for newtwork [" << this->net_name<<"] due to it was user-configured" << std::endl;
return false;
}
// default on: net exists, not on, and not disabled -> copy network config as enabled
std::cout << "[C][network default] Set-up net [" << this->net_name << "] as ["<< (this->default_val == "on" ? "ENABLED" : "DISABLED")
<< "] from template " << template_path << std::endl;
if (flags & F_VERBOSE) std::cout << "[VE][network default] copy file from " << template_path << " to " << template_copy_dest << std::endl;
if (!(flags & F_DRYRUN)) fs::copy(template_path, template_copy_dest);
return true;
};
//force net state
actions["ensure"] = [&]() {
//check for disabled
bool net_enabled = fs::exists(config_path(this->net_name, CFG_GENERAL, CFG_ON));
if (this->default_val == "on" && net_enabled)
{
std::cout << "[C][network ensure on]: Network [" << this->net_name << "] already enabled"<<std::endl;
return false;
}
if (this->default_val == "off" && !net_enabled && fs::exists(config_path(this->net_name, CFG_GENERAL, CFG_OFF)))
{
std::cout << "[C][network ensure off]: Network [" << this->net_name << "] already disabled"<<std::endl;
return false;
}
ENetworkConfigState requested_state, oposite_state;
if (this->default_val == "on") {
requested_state = CFG_ON;
oposite_state = CFG_OFF;
}
if (this->default_val == "off")
{
requested_state = CFG_OFF;
oposite_state = CFG_ON;
}
//move from oposite if exists
if (fs::exists(config_path(this->net_name, CFG_GENERAL, oposite_state))){
std::cout << "[C][network ensure " << this->default_val << "] move file from "
<< config_path(this->net_name, CFG_GENERAL, oposite_state)
<< " to " << config_path(this->net_name, CFG_GENERAL, requested_state)
<< std::endl;
if (!(flags & F_DRYRUN)) fs::rename(config_path(this->net_name, CFG_GENERAL, oposite_state),
config_path(this->net_name, CFG_GENERAL, requested_state));
} else { //copy from template if oposite side of config not exits
std::cout << "[C][network ensure "<<this->default_val << "] copy file from "
<< config_path(this->net_name, CFG_GENERAL, CFG_TEMPLATE)
<< " to " << config_path(this->net_name, CFG_GENERAL, requested_state)
<< std::endl;
if (!(flags & F_DRYRUN)) fs::copy(config_path(this->net_name, CFG_GENERAL, CFG_TEMPLATE),
config_path(this->net_name, CFG_GENERAL, requested_state));
}
return true;
};
//force net state
actions["check"] = [&]() {
//check for disabled
bool net_enabled = fs::exists(config_path(this->net_name, CFG_GENERAL, CFG_ON));
std::cout << "[C][network check]: Network [" << this->net_name << "] " << (net_enabled ? "on" : "off") << std::endl;
return true;
};
return actions[this->action]();
}
#include "AbstractCommand.h"
class CNetworkCommand : public CAbstractScriptCommand {
public:
CNetworkCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CNetworkCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
private:
std::string net_name;
std::string action;
std::string default_val;
};
#include "NetworkListCommand.h"
#include <stdexcept>
#include <filesystem>
#include "../build_config.h"
#include "../config/CellframeConfigFile.h"
namespace fs = std::filesystem;
CAbstractScriptCommand::Registrar<CNetworkListCommand> net_list_registrar("net_list");
CNetworkListCommand::CNetworkListCommand(std::vector <std::string> cmd_tokens)
: CAbstractScriptCommand(cmd_tokens)
{
if (cmd_tokens.size() >= 2)
if(cmd_tokens[1] == "on")
{
m_state = ON;
}
else if(cmd_tokens[1] == "off")
{
m_state = OFF;
}
}
bool CNetworkListCommand::execute(bool non_intercative, int flags)
{
fs::path directoryPath = get_config_path();
if(!fs::exists(directoryPath))
{
std::cout << "The catalog was not found." << std::endl;
return false;
}
if (!fs::is_directory(directoryPath)) {
std::cout << "The specified path is not a directory." << std::endl;
return false;
}
std::vector<NetworkInfo> configs;
const std::string dis = ".dis";
const std::string cfg = ".cfg";
for (const auto& entry : fs::directory_iterator(directoryPath))
{
if(fs::is_regular_file(entry.status()))
{
std::string name = entry.path().filename().string();
auto removeSubstring = [](std::string& str, const std::string& tuRemove)
{
size_t pos = 0;
while ((pos = str.find(tuRemove, pos)) != std::string::npos) {
str.erase(pos, tuRemove.length());
}
};
bool isDis = name.find(dis) != std::string::npos;
bool isCfg = name.find(cfg) != std::string::npos;
if((isDis && isCfg)
&& (m_state == ALL || m_state == OFF) )
{
removeSubstring(name, cfg + dis);
configs.push_back({name, "off"});
}
else if((!isDis && isCfg)
&& (m_state == ALL || m_state == ON))
{
removeSubstring(name, cfg);
configs.push_back({name, "on"});
}
else if((!isDis && !isCfg)
&& m_state == ALL)
{
configs.push_back({name, "unknown"});
}
}
}
if(configs.empty())
{
std::cout << "No networks were found.";
}
else
{
for (const auto& item : configs) {
std::cout << "name: " <<item.networkName << "\t state:" << item.state << std::endl;
}
}
return true;
}
#pragma once
#include "AbstractCommand.h"
class CNetworkListCommand : public CAbstractScriptCommand {
enum NetworState{
ALL = 0,
ON,
OFF
};
struct NetworkInfo
{
std::string networkName = "";
std::string state = "";
};
public:
CNetworkListCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CNetworkListCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
private:
NetworState m_state = ALL;
};
#include "ServiceCommand.h"
#include <stdexcept>
#include <filesystem>
#include "../build_config.h"
#include "../config/CellframeConfigFile.h"
#include "../service/service.h"
namespace fs = std::filesystem;
CAbstractScriptCommand::Registrar<CServiceCommand> service_registrar("service");
CServiceCommand::CServiceCommand(std::vector <std::string> cmd_tokens): CAbstractScriptCommand(cmd_tokens)
{
//zero token is always a command (network)
if (cmd_tokens.size() < 2)
throw std::invalid_argument("service command require action argument");
this->action = cmd_tokens[1];
}
bool CServiceCommand::execute(bool non_intercative, int flags)
{
if (this->action == "enable"){
if (CServiceControl::enable()) {
std::cout << "enabled" <<std::endl;
}
else{
std::cout << "error" <<std::endl;
}
}
if (this->action == "disable")
{
if (CServiceControl::disable()) {
std::cout << "disabled" <<std::endl;
}
else{
std::cout << "error" <<std::endl;
}
}
if (this->action == "status")
{
unsigned int status = CServiceControl::serviceStatus();
if (status & SERVICE_ENABLED)
std::cout << "service: enabled" << std::endl;
else
std::cout << "service: disabled" << std::endl;
if (status & PROCESS_RUNNING)
std::cout << "process: running" << std::endl;
else
std::cout << "process: notfound" << std::endl;
}
if (this->action == "start")
{
if (CServiceControl::start())
{
std::cout << "started" << std::endl;
}
else
{
std::cout << "error" << std::endl;
}
}
if (this->action == "stop")
{
if (CServiceControl::stop())
{
std::cout << "stoped" << std::endl;
}
else
{
std::cout << "error" << std::endl;
}
}
if (this->action == "restart")
{
if (CServiceControl::restart())
{
std::cout << "restarted" << std::endl;
}
else
{
std::cout << "error" << std::endl;
}
}
return true;
}
#include "AbstractCommand.h"
class CServiceCommand : public CAbstractScriptCommand {
public:
CServiceCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CServiceCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
private:
std::string action;
};
#include "StorageCommand.h"
#include "../config/CellframeConfigFile.h"
CAbstractScriptCommand::Registrar<CVariableCommand> var_registrar("var");
CVariableCommand::CVariableCommand(std::vector <std::string> cmd_tokens):CAbstractScriptCommand(cmd_tokens)
{
std::string joined_condition = string_join(std::vector<std::string>(cmd_tokens.begin()+1,cmd_tokens.end()), "");
std::vector < std::string> tokens = tokenize(joined_condition, std::regex("="));
if (tokens.size() != 2) throw std::invalid_argument("var command uses exactly VAR=VAL syntax");
this->var = tokens[0];
this->val = tokens[1];
}
bool CVariableCommand::execute(bool non_intercative, int flags)
{
std::string real_val = substitute_variables(this->val);
variable_storage[this->var] = real_val;
if (flags & F_VERBOSE)
{
std::cout <<"[VC] Set "<<real_val<< " as " << this->var << ", current stor = {";
for (auto a: variable_storage) {
std::cout << a.first << " : " << a.second<<", ";
}
std::cout << "}"<<std::endl;
}
return true;
}
#pragma once
#include "AbstractCommand.h"
class CVariableCommand : public CAbstractScriptCommand {
public:
CVariableCommand(std::vector <std::string> cmd_tokens);
static std::unique_ptr<CAbstractScriptCommand> create(std::vector <std::string> cmd_tokens) { return std::make_unique<CVariableCommand>(cmd_tokens); }
bool execute(bool non_interactive, int flags);
private:
std::string var;
std::string val;
};
#include "CellframeConfigFile.h"
#include <any>
#include <chrono>
#include <functional>
#include <iomanip>
#include <map>
#include <memory>
#include <regex>
#include <stdexcept>
#include <string>
#include <vector>
namespace utils {
std::string escape(const std::string &str);
struct IPlaceholder {
virtual std::string resolve(const std::vector<std::any> &args) const = 0;
virtual const std::string &getPattern() const = 0;
virtual ~IPlaceholder() = default;
};
class ArgCountError : public std::runtime_error {
public:
explicit ArgCountError(const std::string &msg) : std::runtime_error(msg) {}
ArgCountError() : std::runtime_error("Wrong number of arguments provided") {}
};
template <typename... Args>
class Placeholder : public IPlaceholder {
public:
using FuncType = std::function<std::string(Args...)>;
Placeholder(std::string p, FuncType r) : pattern_(std::move(p)), resolver_(std::move(r)) {}
const std::string &getPattern() const override { return pattern_; }
std::string resolve(const std::vector<std::any> &args) const override {
if (args.size() != sizeof...(Args)) throw ArgCountError();
return invoke(args, std::index_sequence_for<Args...>{});
}
private:
std::string pattern_;
FuncType resolver_;
template <size_t... I>
std::string invoke(std::vector<std::any> const &args, std::index_sequence<I...>) const {
return resolver_(std::any_cast<Args>(args[I])...);
}
};
class SubstitutionError : public std::runtime_error {
public:
explicit SubstitutionError(const std::string &msg): std::runtime_error(msg) {}
SubstitutionError() : std::runtime_error("Error executing the replacing of the placeholder") {
}
};
class PlaceholderManager {
public:
typedef std::string(EscapingFnctTp)(const std::string &str);
void addPlaceholder(const std::shared_ptr<IPlaceholder> &placeholder) {
placeholders_[placeholder->getPattern()] = placeholder;
}
void setEscapingFnct(std::function<EscapingFnctTp> escapingFnct) {
escapingFnct_ = escapingFnct;
}
std::string replacePlaceholders(
std::string input, const std::map<std::string, std::vector<std::any>> &args = {}) {
for (const auto &itPh : placeholders_)
{
replaceEachPh(input, args, itPh.second);
}
return input;
}
private:
std::map<std::string, std::shared_ptr<IPlaceholder>> placeholders_;
void replaceEachPh(std::string &input,
const std::map<std::string, std::vector<std::any>> &args,
const std::shared_ptr<IPlaceholder> &ph) {
static const std::vector<std::any> empty{};
const std::string &phStr = ph->getPattern();
std::regex regex(escapingFnct_(phStr));
auto it = args.find(phStr);
const std::vector<std::any> &vArgs = it != args.end() ? it->second : empty;
std::string fmt;
try {
fmt = ph->resolve(vArgs);
try {
input = std::regex_replace(input, regex, fmt);
} catch (...) {
throw SubstitutionError();
}
} catch (const ArgCountError &) {
}
}
private:
std::function<EscapingFnctTp> escapingFnct_{utils::escape};
};
inline std::string escape(const std::string &str) {
std::regex exp("\\{");
std::string res = std::regex_replace(str, exp, "\\{");
exp = std::regex("\\}");
res = std::regex_replace(res, exp, "\\}");
return res;
}
}
fs::path get_config_path(EPathConfigType pathType)
{
fs::path resultPath;
switch (pathType)
{
case CFG_NODE:
resultPath = fs::path{variable_storage["CONFIGS_PATH"]}/"share"/"configs";
break;
case CFG_NODE_TEMPLATE:
resultPath = fs::path{variable_storage["CONFIGS_PATH"]}/"etc";
break;
case CFG_NETWORK:
resultPath = fs::path{variable_storage["CONFIGS_PATH"]}/"etc"/"network/";
break;
case CFG_NETWORK_TEMPLATE:
resultPath = fs::path{variable_storage["CONFIGS_PATH"]}/"share"/"configs"/"network/";
break;
default:
break;
}
return resultPath;
}
fs::path config_path(const std::string &name, ENetworkConfigType type, ENetworkConfigState state) {
if (name == "cellframe-node")
{ if (state == CFG_TEMPLATE)
return fs::path{variable_storage["CONFIGS_PATH"]}/"share"/"configs"/"cellframe-node.cfg";
else
return fs::path{variable_storage["CONFIGS_PATH"]}/"etc"/"cellframe-node.cfg";
}
switch (type)
{
case CFG_GENERAL:
if (state == CFG_ON) return fs::path{variable_storage["CONFIGS_PATH"]}/"etc"/"network/"/(name + ".cfg");
if (state == CFG_OFF) return fs::path{variable_storage["CONFIGS_PATH"]}/"etc"/"network/"/(name + ".cfg.dis");
if (state == CFG_TEMPLATE) return fs::path{variable_storage["CONFIGS_PATH"]}/"share"/"configs"/"network/"/(name + ".cfg");
break;
case CFG_MAINCHAIN:
return fs::path{variable_storage["CONFIGS_PATH"]}/"etc"/"network/"/name/"main.cfg";
case CFG_ZEROCHAIN:
return fs::path{variable_storage["CONFIGS_PATH"]}/"etc"/"network/"/name/"zerochain.cfg";
}
throw std::invalid_argument("cfg for such params cant be detectd");
}
CellframeConfigurationFile::CellframeConfigurationFile(fs::path filepath, int flags):flags(flags) {
if (!fs::exists(filepath))
throw std::invalid_argument(string_format("Config path [%s] not exitsts", filepath.c_str()));
std::ifstream infile(filepath);
size_t line_num = 0;
for( std::string line; std::getline( infile, line ); line_num++)
{
this->lines.push_back(line);
}
if (flags & F_VERBOSE) std::cout << "[VC] Loaded "<<line_num<<" lines form " << filepath << std::endl;
this->path = filepath;
}
bool is_group_decl(const std::string line, std::string *gname)
{
std::string trimmed_line = line;
auto tokens = tokenize(trimmed_line, std::regex("="));
if (tokens.size()>1) return false;
if (tokens[0][0]!='[') return false;
std::string group_name = tokens[0].substr(tokens[0].find('[')+1, tokens[0].find(']')-1);
trim(group_name);
if (group_name.empty()) return false;
if (gname) *gname = group_name;
return true;
}
struct cfg_pair {
std::string param;
std::string val;
};
cfg_pair parse_param_value(const std::string line)
{
std::string trimmed_line = line;
if (line.find("=") == line.npos) throw std::invalid_argument("config line expected to be key=value");
auto tokens = tokenize(trimmed_line, std::regex("="));
trim(tokens[0]);
if (tokens.size() < 2)
tokens.push_back("");
return {tokens[0], tokens[1]};
}
bool CellframeConfigurationFile::exists(const std::string & group, const std::string & param, std::string *value, int *line_no, bool *group_exists)
{
if (flags & F_VERBOSE) std::cout << "[VC] Check for existanse [" << group<<"] "<<param << " in "<<this->path <<std::endl;
bool group_found = false;
int current_line = -1;
std::string group_name;
for( auto line : lines)
{
current_line ++;
trim(line);
line = line.substr(0, line.find("#")); //skip comments
if(line.empty()) continue; //skip empties
bool is_grp_decl = is_group_decl(line, &group_name);
if (is_grp_decl && !group_found)
{
if (flags & F_VERBOSE) std::cout << "[VC] Found group [" << group_name<<"] " << std::endl;
if (group_name == group) group_found = true;
continue;
}
if (is_grp_decl && group_found)
{
if (flags & F_VERBOSE) std::cout << "[VC] No param in group [" << group_name<<"], group ends at " << current_line-1 << std::endl;
if (line_no) *line_no = current_line-1;
if (group_exists) *group_exists = group_found;
return false;
}
if (!group_found) continue;
auto res = parse_param_value(line);
if (res.param == param)
{
if (flags & F_VERBOSE) std::cout << "[VC] in group [" << group_name<<"] found "
<< res.param
<< ":" <<res.val
<< " at line "
<< current_line << std::endl;
if (group_exists) *group_exists = group_found;
if (value) *value = res.val;
if (line_no) *line_no = current_line;
return true;
}
}
if (line_no) *line_no = current_line-1;
if (group_exists)
*group_exists = group_found;
return false;
}
std::string CellframeConfigurationFile::set(const std::string & group, const std::string & param, const std::string &value)
{
if (flags & F_VERBOSE) std::cout << "[VC] set ["
<< group << "] "<<param<<"="<<value << std::endl;
int line_set_to;
bool group_exists = false;
bool param_exists = exists(group, param, nullptr, &line_set_to, &group_exists);
if (param_exists) lines[line_set_to] = param+"="+value;
else
{
if (!group_exists)
{
lines.emplace(lines.begin()+line_set_to, "["+group+"]");
line_set_to += 1;
}
lines.emplace(lines.begin()+line_set_to, param+"="+value);
lines.emplace(lines.begin()+line_set_to + 1, "\n");
}
return lines[line_set_to];
}
bool CellframeConfigurationFile::save()
{
if (flags & F_VERBOSE) std::cout << "[VC] saving " << this->lines.size() << " lines to " << this->path << std::endl;
if (flags & F_DRYRUN) {
for (auto l : lines) std::cout << l << std::endl;
return true;
};
std::ofstream file(path);
for (auto l : lines) file << l << std::endl;
file.close();
return true;
}
void CellframeConfigurationFile::replace_placeholders(std::map<std::string, std::string> data)
{
if (flags & F_VERBOSE) std::cout << "[VC] replacing placeholders in" << this->path<< std::endl;
int current_line = -1;
for( auto line : lines)
{
current_line ++;
trim(line);
line = line.substr(0, line.find("#")); //skip comments
if(line.empty()) continue; //skip empties
auto nline = substitute_variables(line);
lines[current_line] = nline;
}
}
std::string substitute_variables(const std::string &string)
{
utils::PlaceholderManager pman;
for (auto var: variable_storage){
pman.addPlaceholder(std::make_shared<utils::Placeholder<>>(
std::string("\\${"+var.first+"}"), [=] { return var.second; }));
}
return pman.replacePlaceholders(string);
}