diff --git a/CMakeLists.txt b/CMakeLists.txt index ad6393f..f55d3eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,13 +44,11 @@ add_dependencies(sagent ) add_custom_command(TARGET sagent POST_BUILD - COMMAND cp ./dependencies/bin/hpws ./build/ - COMMAND cp ./dependencies/bin/hpfs ./build/ - COMMAND cp -r ./dependencies/default_contract ./build/ - COMMAND cp ./bootstrap-contract/script.sh ./build/default_contract/contract_fs/seed/state/script.sh - COMMAND cp ./build/bootstrap_contract ./build/default_contract/contract_fs/seed/state/bootstrap_contract - COMMAND cp ./installer/user-install.sh ./build/user-install.sh - COMMAND cp ./installer/user-uninstall.sh ./build/user-uninstall.sh + COMMAND cp -r ./dependencies/* ./build/ + COMMAND tar xf ./build/contract_template.tar -C ./build/ --no-same-owner && rm ./build/contract_template.tar + COMMAND mv ./build/hp.cfg ./build/contract_template/cfg/ + COMMAND cp ./bootstrap-contract/script.sh ./build/contract_template/contract_fs/seed/state/ + COMMAND mv ./build/bootstrap_contract ./build/contract_template/contract_fs/seed/state/ ) target_precompile_headers(sagent PUBLIC src/pchheader.hpp) diff --git a/README.md b/README.md index fab61cc..c48ffdb 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,8 @@ A C++ version of sashimono agent * Concurrent Queue - https://github.com/cameron314/concurrentqueue * Boost Stacktrace - https://www.boost.org -## Setting up Sashimono Agent environment -- Place a hotpocket contract named **default_contract** (Copies of this contract will be made when new instances are created) inside the **dependencies** directory. This should be a new contract (Created by `hpcore new`). In the future this will be placed by the installation process. - -- Run the setup script located at the repo root (tested on Ubuntu 18.04). +## Setting up Sashimono Agent development environment +Run the setup script located at the repo root (tested on Ubuntu 18.04). ``` ./dev-setup.sh ``` diff --git a/dependencies/.gitignore b/dependencies/.gitignore deleted file mode 100644 index b5d15ce..0000000 --- a/dependencies/.gitignore +++ /dev/null @@ -1 +0,0 @@ -default_contract \ No newline at end of file diff --git a/dependencies/bin/hpfs b/dependencies/bin/hpfs deleted file mode 100755 index 512fb88..0000000 Binary files a/dependencies/bin/hpfs and /dev/null differ diff --git a/dependencies/contract_template.tar b/dependencies/contract_template.tar new file mode 100644 index 0000000..2bc397e Binary files /dev/null and b/dependencies/contract_template.tar differ diff --git a/dependencies/hp.cfg b/dependencies/hp.cfg new file mode 100644 index 0000000..165108e --- /dev/null +++ b/dependencies/hp.cfg @@ -0,0 +1,91 @@ +{ + "hp_version": "1.0.0", + "node": { + "public_key": "ed903ba38b1fda781e048aee8cf805afa3e8dfb39dac4cd97ee5d00976d3a4808b", + "private_key": "ed433d9ad4d6294254e4e307081508fc37327f62a35d6bd0c765187c77cff978e2903ba38b1fda781e048aee8cf805afa3e8dfb39dac4cd97ee5d00976d3a4808b", + "role": "validator", + "history": "custom", + "history_config": { + "max_primary_shards": 1, + "max_raw_shards": 1 + } + }, + "contract": { + "id": "f15abd89-ea82-4d8b-8d11-29fbc665dcf6", + "execute": true, + "run_as": "", + "log": { + "enable": true, + "max_mbytes_per_file": 5, + "max_file_count": 10 + }, + "version": "1.0", + "unl": [ + "ed903ba38b1fda781e048aee8cf805afa3e8dfb39dac4cd97ee5d00976d3a4808b" + ], + "bin_path": "/usr/bin/node", + "bin_args": "/contract/echo_contract.js", + "roundtime": 2000, + "stage_slice": 25, + "consensus": "private", + "npl": "private", + "max_input_ledger_offset": 10, + "appbill": { + "mode": "", + "bin_args": "" + }, + "round_limits": { + "user_input_bytes": 0, + "user_output_bytes": 0, + "npl_output_bytes": 0, + "proc_cpu_seconds": 0, + "proc_mem_bytes": 0, + "proc_ofd_count": 0 + } + }, + "mesh": { + "port": 22860, + "listen": true, + "idle_timeout": 120000, + "known_peers": [], + "msg_forwarding": true, + "max_connections": 0, + "max_known_connections": 0, + "max_in_connections_per_host": 0, + "max_bytes_per_msg": 0, + "max_bytes_per_min": 0, + "max_bad_msgs_per_min": 0, + "max_bad_msgsigs_per_min": 0, + "max_dup_msgs_per_min": 0, + "peer_discovery": { + "enabled": true, + "interval": 30000 + } + }, + "user": { + "port": 8080, + "listen": true, + "idle_timeout": 0, + "max_bytes_per_msg": 0, + "max_bytes_per_min": 0, + "max_bad_msgs_per_min": 0, + "max_connections": 0, + "max_in_connections_per_host": 0, + "concurrent_read_reqeuests": 4 + }, + "hpfs": { + "external": false, + "log": { + "log_level": "wrn" + } + }, + "log": { + "log_level": "inf", + "max_mbytes_per_file": 5, + "max_file_count": 10, + "loggers": [ + "file", + "console" + ] + } +} diff --git a/dependencies/hpfs b/dependencies/hpfs new file mode 100755 index 0000000..5c1d2b3 Binary files /dev/null and b/dependencies/hpfs differ diff --git a/dependencies/bin/hpws b/dependencies/hpws similarity index 100% rename from dependencies/bin/hpws rename to dependencies/hpws diff --git a/installer/user-install.sh b/dependencies/user-install.sh similarity index 98% rename from installer/user-install.sh rename to dependencies/user-install.sh index 410307b..74d093f 100755 --- a/installer/user-install.sh +++ b/dependencies/user-install.sh @@ -25,9 +25,10 @@ docker_bin=/usr/bin/sashimono-agent/dockerbin # Check cgroup mounts exists. ([ ! -d /sys/fs/cgroup/cpu ] || [ ! -d /sys/fs/cgroup/memory ]) && echo "CGROUP_ERR,INST_ERR" && exit 1 +script_dir=$(pwd) function rollback() { echo "Rolling back user installation. $1" - $(pwd)/user-uninstall.sh $user + $script_dir/user-uninstall.sh $user echo "Rolled back the installation." echo "$1,INST_ERR" && exit 1 } diff --git a/installer/user-uninstall.sh b/dependencies/user-uninstall.sh similarity index 98% rename from installer/user-uninstall.sh rename to dependencies/user-uninstall.sh index db88f1d..e551277 100755 --- a/installer/user-uninstall.sh +++ b/dependencies/user-uninstall.sh @@ -6,7 +6,6 @@ user=$1 # Check whether this is a valid sashimono username. prefix="sashi" [ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$prefix[0-9]+$ ]] && echo "ARGS,UNINST_ERR" && exit 1 -group="sashimonousers" cgroupsuffix="-cg" user_dir=/home/$user diff --git a/examples/message-board/message-board.js b/examples/message-board/message-board.js index 73e6457..3039b20 100644 --- a/examples/message-board/message-board.js +++ b/examples/message-board/message-board.js @@ -49,15 +49,15 @@ const sendToAll = (msg) => { }); } -const askForInput = (label) => { +const askForInput = (label, defaultValue) => { return new Promise(resolve => { rl.question(`${label}? `, (input) => { - resolve(input); + resolve(input && input.length > 0 ? input : defaultValue); }) }) } -server.listen(8080, () => { +server.listen(5000, () => { console.log(`wss://localhost:${server.address().port}`) console.log("Ready to accept inputs."); @@ -71,7 +71,8 @@ server.listen(8080, () => { else { switch (inp) { case 'create': - contractId = await askForInput('Contract ID'); + contractId = await askForInput('Contract ID (optional)', uuidv4()); + sendToAll(JSON.stringify({ id: uuidv4(), type: 'create', @@ -81,15 +82,15 @@ server.listen(8080, () => { break; case 'initiate': containerName = await askForInput('Container Name'); - role = await askForInput('Role or '); - if (role && role != 'validator' && role != 'observer') { + role = await askForInput('Role or ', "validator"); + if (role != 'validator' && role != 'observer') { console.error('Invalid role. (Should be "validator" or "observer").') break; } - history = await askForInput('History '); + + history = await askForInput('History ', "custom,1,1"); split = []; - if (history) - { + if (history) { split = history.split(','); if (split.length == 0 || split.length == 0 > 3) { console.error('Invalid history.') diff --git a/installer/sashimono-install.sh b/installer/sashimono-install.sh index 31853cc..759e64a 100755 --- a/installer/sashimono-install.sh +++ b/installer/sashimono-install.sh @@ -29,26 +29,14 @@ fi # Install Sashimono agent binaries into sashimono bin dir. # TODO. -# Copy necessary files into sashimono data folder. -cp -r ../dependencies/default_contract $sashimono_data -if ! cp ../bootstrap-contract/script.sh $sashimono_data/default_contract/contract_fs/seed/state/script.sh; then - echo "script.sh file not found." - exit 1 -fi - -if ! cp ../build/bootstrap_contract $sashimono_data/default_contract/contract_fs/seed/state/bootstrap_contract; then - echo "bootstrap_contract file not found." - exit 1 -fi - # Download docker packages into a tmp dir and extract into docker bin. echo "Installing rootless docker packages into $docker_bin" -installer_dir=$(pwd) tmp=$(mktemp -d) +script_dir=$(pwd) function rollback() { echo "Rolling back sashimono installation." - $installer_dir/sashimono-uninstall.sh + $script_dir/sashimono-uninstall.sh [ -d $tmp ] && rm -r $tmp echo "Rolled back the installation." exit 1 diff --git a/src/comm/comm_session.cpp b/src/comm/comm_session.cpp index c98e6b1..b32a144 100644 --- a/src/comm/comm_session.cpp +++ b/src/comm/comm_session.cpp @@ -172,17 +172,17 @@ namespace comm { std::string id, type; if (msg_parser.parse(msg) == -1 || msg_parser.extract_type_and_id(type, id) == -1) - __HANDLE_RESPONSE("", "error", "Invalid message.", -1); + __HANDLE_RESPONSE("", "error", "format_error", -1); if (type == msg::MSGTYPE_CREATE) { msg::create_msg msg; if (msg_parser.extract_create_message(msg) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_CREATE_RES, "Invalid message.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_CREATE_RES, "format_error", -1); hp::instance_info info; if (hp::create_new_instance(info, msg.pubkey, msg.contract_id) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_CREATE_RES, "Error creating instance.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_CREATE_RES, "create_error", -1); std::string create_res; msg_parser.build_create_response(create_res, info); @@ -192,48 +192,48 @@ namespace comm { msg::initiate_msg msg; if (msg_parser.extract_initiate_message(msg) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_INITIATE_RES, "Invalid message.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_INITIATE_RES, "format_error", -1); if (hp::initiate_instance(msg.container_name, msg) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_INITIATE_RES, "Error initiating instance.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_INITIATE_RES, "init_error", -1); - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_INITIATE_RES, "Initiated", 0); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_INITIATE_RES, "initiated", 0); } else if (type == msg::MSGTYPE_DESTROY) { msg::destroy_msg msg; if (msg_parser.extract_destroy_message(msg)) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_DESTROY_RES, "Invalid message.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_DESTROY_RES, "format_error", -1); if (hp::destroy_container(msg.container_name) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_DESTROY_RES, "Error destroying instance.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_DESTROY_RES, "destroy_error", -1); - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_DESTROY_RES, "Destroyed", 0); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_DESTROY_RES, "destroyed", 0); } else if (type == msg::MSGTYPE_START) { msg::start_msg msg; if (msg_parser.extract_start_message(msg)) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_START_RES, "Invalid message.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_START_RES, "format_error", -1); if (hp::start_container(msg.container_name) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_START_RES, "Error starting instance.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_START_RES, "start_error", -1); - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_START_RES, "Started", 0); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_START_RES, "started", 0); } else if (type == msg::MSGTYPE_STOP) { msg::stop_msg msg; if (msg_parser.extract_stop_message(msg)) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_STOP_RES, "Invalid message.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_STOP_RES, "format_error", -1); if (hp::stop_container(msg.container_name) == -1) - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_STOP_RES, "Error stopping instance.", -1); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_STOP_RES, "stop_error", -1); - __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_STOP_RES, "Stopped", 0); + __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_STOP_RES, "stopped", 0); } else - __HANDLE_RESPONSE(id, "error", "Invalid message type.", -1); + __HANDLE_RESPONSE(id, "error", "type_error", -1); return 0; } diff --git a/src/conf.cpp b/src/conf.cpp index a9c6f90..b334a56 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -1,5 +1,6 @@ #include "conf.hpp" #include "util/util.hpp" +#include "version.hpp" namespace conf { @@ -34,19 +35,15 @@ namespace conf */ int create() { - if (util::is_dir_exists(ctx.config_dir)) + if (util::is_file_exists(ctx.config_file)) { - if (util::is_file_exists(ctx.config_file)) - { - std::cerr << "Config file already exists. Cannot create config at the same location.\n"; - return -1; - } + std::cerr << "Config file already exists. Cannot create config at the same location.\n"; + return -1; } else { // Recursivly create contract directory. Return an error if unable to create - if (util::create_dir_tree_recursive(ctx.config_dir) == -1 || - util::create_dir_tree_recursive(ctx.log_dir) == -1 || + if (util::create_dir_tree_recursive(ctx.log_dir) == -1 || util::create_dir_tree_recursive(ctx.data_dir) == -1) { std::cerr << "ERROR: unable to create directories.\n"; @@ -59,10 +56,11 @@ namespace conf { sa_config cfg = {}; - cfg.version = "0.0.1"; + cfg.version = version::AGENT_VERSION; + cfg.hp.host_address = "127.0.0.1"; cfg.hp.init_peer_port = 22861; cfg.hp.init_user_port = 8081; - cfg.server.ip_port = {}; + cfg.server.ip_port = {"127.0.0.1", 5000}; cfg.system.max_instance_count = 10; cfg.system.max_mem_kbytes = cfg.system.max_instance_count * 50 * 1024; // 50MB per instance, Minimum allowed by single docker image is 6MB @@ -90,31 +88,30 @@ namespace conf * This is called after parsing SA command line arg in order to populate the ctx. * @param exepath Path to executable. */ - void set_dir_paths(std::string exepath) + void set_dir_paths(std::string exepath, std::string datadir) { if (exepath.empty()) { // This code branch will never execute the way main is currently coded, but it might change in future - std::cerr << "Executeble path must be specified\n"; + std::cerr << "Executable path must be specified\n"; exit(1); } - // resolving the path through realpath will remove any trailing slash if present + // Resolve the directory containing executables. exepath = util::realpath(exepath); - - // Take the parent directory path. ctx.exe_dir = dirname(exepath.data()); + + // If data dir is not specified, use the same dir as executables. + ctx.data_dir = datadir.empty() ? ctx.exe_dir : util::realpath(datadir); + ctx.hpws_exe_path = ctx.exe_dir + "/hpws"; ctx.hpfs_exe_path = ctx.exe_dir + "/hpfs"; ctx.user_install_sh = ctx.exe_dir + "/user-install.sh"; ctx.user_uninstall_sh = ctx.exe_dir + "/user-uninstall.sh"; - const std::string sashimono_folder = conf::ctx.environment == conf::ENVIRONMENT::DEVELOPMENT ? ctx.exe_dir : "/etc/sashimono"; - ctx.default_contract_path = sashimono_folder + "/default_contract"; - ctx.config_dir = sashimono_folder + "/cfg"; - ctx.config_file = ctx.config_dir + "/sa.cfg"; - ctx.log_dir = sashimono_folder + "/log"; - ctx.data_dir = sashimono_folder + "/data"; + ctx.contract_template_path = ctx.data_dir + "/contract_template"; + ctx.config_file = ctx.data_dir + "/sa.cfg"; + ctx.log_dir = ctx.data_dir + "/log"; } /** @@ -128,7 +125,7 @@ namespace conf ctx.log_dir, ctx.data_dir, ctx.hpws_exe_path, - ctx.default_contract_path, + ctx.contract_template_path, ctx.user_install_sh, ctx.user_uninstall_sh}; diff --git a/src/conf.hpp b/src/conf.hpp index daed9ef..8615c61 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -42,11 +42,11 @@ namespace conf struct log_config { - std::string log_level; // Log severity level (dbg, inf, wrn, wrr) - LOG_SEVERITY log_level_type; // Log severity level enum (debug, info, warn, error) - std::unordered_set loggers; // List of enabled loggers (console, file) - size_t max_mbytes_per_file = 0; // Max MB size of a single log file. - size_t max_file_count = 0; // Max no. of log files to keep. + std::string log_level; // Log severity level (dbg, inf, wrn, wrr) + LOG_SEVERITY log_level_type = LOG_SEVERITY::INFO; // Log severity level enum (debug, info, warn, error) + std::unordered_set loggers; // List of enabled loggers (console, file) + size_t max_mbytes_per_file = 0; // Max MB size of a single log file. + size_t max_file_count = 0; // Max no. of log files to keep. }; struct server_config @@ -57,8 +57,8 @@ namespace conf struct hp_config { std::string host_address; - uint16_t init_peer_port; - uint16_t init_user_port; + uint16_t init_peer_port = 0; + uint16_t init_user_port = 0; }; struct system_config @@ -78,25 +78,17 @@ namespace conf log_config log; }; - enum ENVIRONMENT - { - DEVELOPMENT, - PRODUCTION - }; - struct sa_context { - std::string command; // The CLI command issued to launch Sashimono agent - ENVIRONMENT environment = ENVIRONMENT::DEVELOPMENT; // Running environment mode. (dev | prod) - std::string exe_dir; // Hot Pocket executable dir. - std::string hpws_exe_path; // hpws executable file path. - std::string hpfs_exe_path; // hpfs executable file path. - std::string default_contract_path; // Path to default contract. + std::string command; // The CLI command issued to launch Sashimono agent + std::string exe_dir; // Sashimono Agent executable dir. + std::string hpws_exe_path; // hpws executable file path. + std::string hpfs_exe_path; // hpfs executable file path. + std::string contract_template_path; // Path to default contract. std::string user_install_sh; std::string user_uninstall_sh; - std::string config_dir; // Config dir full path. std::string config_file; // Full path to the config file. std::string log_dir; // Log directory full path. std::string data_dir; // Data directory full path. @@ -114,7 +106,7 @@ namespace conf int create(); - void set_dir_paths(std::string exepath); + void set_dir_paths(std::string exepath, std::string datadir); int validate_dir_paths(); diff --git a/src/hp_manager.cpp b/src/hp_manager.cpp index e9d5d50..abb37ef 100644 --- a/src/hp_manager.cpp +++ b/src/hp_manager.cpp @@ -42,11 +42,11 @@ namespace hp */ int init() { - const std::string db_path = conf::ctx.data_dir + "/hp_instances.sqlite"; + const std::string db_path = conf::ctx.data_dir + "/sa.sqlite"; if (sqlite::open_db(db_path, &db, true) == -1 || sqlite::initialize_hp_db(db) == -1) { - LOG_ERROR << "Error preparing hp database in " << db_path; + LOG_ERROR << "Error preparing database in " << db_path; return -1; } @@ -510,19 +510,20 @@ namespace hp LOG_ERROR << errno << ": Error creating temporary directory to create contract folder."; return -1; } - const std::string source_path = conf::ctx.default_contract_path + "/*"; + const std::string source_path = conf::ctx.contract_template_path + "/*"; int len = 25 + source_path.length(); char cp_command[len]; sprintf(cp_command, COPY_DIR, source_path.data(), temp_dirpath); if (system(cp_command) != 0) { - LOG_ERROR << "Default contract copying failed to " << temp_dirpath; + LOG_ERROR << errno << ": Default contract copying failed to " << temp_dirpath; return -1; } + const std::string config_dir = std::string(temp_dirpath) + "/cfg"; + // Read the config file into json document object. - std::string config_file_path(temp_dirpath); - config_file_path.append("/cfg/hp.cfg"); + const std::string config_file_path = config_dir + "/hp.cfg"; const int config_fd = open(config_file_path.data(), O_RDWR, FILE_PERMS); if (config_fd == -1) @@ -571,6 +572,16 @@ namespace hp } close(config_fd); + // Generate tls key files using openssl command is available. + const std::string tls_command = "openssl req -newkey rsa:2048 -new -nodes -x509 -days 365 -keyout " + + config_dir + "/tlskey.pem" + " -out " + config_dir + "/tlscert.pem " + + "-subj \"/C=HP/ST=HP/L=HP/O=HP/CN=" + std::string(username) + "\""; + if (system(tls_command.c_str()) != 0) + { + LOG_ERROR << errno << ": Error generting tls key files at " << config_dir; + return -1; + } + // Move the contract to contract dir len = 22 + contract_dir.length(); char mv_command[len]; diff --git a/src/hpfs_manager.cpp b/src/hpfs_manager.cpp index adf08cd..5ab1da3 100644 --- a/src/hpfs_manager.cpp +++ b/src/hpfs_manager.cpp @@ -86,14 +86,16 @@ namespace hpfs dup2(fd, STDERR_FILENO); close(fd); - std::string trace_arg = "trace="; - trace_arg.append(log_level); + const std::string ugid_arg = "ugid="; + const std::string trace_arg = "trace=" + std::string(log_level); + char *execv_args[] = { conf::ctx.hpfs_exe_path.data(), (char *)"fs", (char *)fs_dir.data(), (char *)mount_dir.data(), (char *)(merge ? "merge=true" : "merge=false"), + (char *)ugid_arg.data(), (char *)trace_arg.data(), NULL}; diff --git a/src/main.cpp b/src/main.cpp index 8777ef2..62f6c49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,15 +9,16 @@ #include "hp_manager.hpp" #include "crypto.hpp" #include "hp_manager.hpp" +#include "version.hpp" -#define PARSE_ERROR \ - { \ - std::cerr << "Arguments mismatch.\n"; \ - std::cout << "Usage:\n"; \ - std::cout << "sagent version\n"; \ - std::cout << "sagent [command = run | new | rekey][env(optional) = dev | prod]\n"; \ - std::cout << "Example: sagent run\n"; \ - return -1; \ +#define PARSE_ERROR \ + { \ + std::cerr << "Arguments mismatch.\n"; \ + std::cerr << "Usage:\n"; \ + std::cerr << "sagent version\n"; \ + std::cerr << "sagent [data_dir]\n"; \ + std::cerr << "Example: sagent run /etc/sashimono\n"; \ + return -1; \ } /** @@ -32,19 +33,14 @@ int parse_cmd(int argc, char **argv) { conf::ctx.command = argv[1]; - if (argc == 3) - { - const std::string env(argv[2]); - if (env != "dev" && env != "prod") - PARSE_ERROR - - conf::ctx.environment = (env == "dev") ? conf::ENVIRONMENT::DEVELOPMENT : conf::ENVIRONMENT::PRODUCTION; - } - - if (conf::ctx.command == "new" || conf::ctx.command == "run" || conf::ctx.command == "version") + if (conf::ctx.command == "new" || conf::ctx.command == "run") { // We populate the global contract ctx with the detected command. - conf::set_dir_paths(argv[0]); + conf::set_dir_paths(argv[0], (argc == 3) ? argv[2] : ""); + return 0; + } + else if (conf::ctx.command == "version") + { return 0; } } @@ -131,13 +127,17 @@ int main(int argc, char **argv) if (parse_cmd(argc, argv) != 0) return -1; + if (conf::ctx.command == "version") + { + std::cout << "Sashimono Agent version " << version::AGENT_VERSION << "\n"; + } if (conf::ctx.command == "new") { // This will create a new config. if (conf::create() != 0) return -1; } - else + else if (conf::ctx.command == "run") { if (conf::init() != 0) return -1; @@ -147,33 +147,27 @@ int main(int argc, char **argv) if (crypto::init() == -1) return -1; - if (conf::ctx.command == "run") + LOG_INFO << "Sashimono agent (version " << version::AGENT_VERSION << ")"; + LOG_INFO << "Log level: " << conf::cfg.log.log_level; + LOG_INFO << "Data dir: " << conf::ctx.data_dir; + + if (comm::init() == -1 || hp::init() == -1) { - LOG_INFO << "Sashimono agent started." - << " Version: " << conf::cfg.version - << " | Log level: " << conf::cfg.log.log_level - << " | Env: " << (conf::ctx.environment == conf::ENVIRONMENT::DEVELOPMENT ? "development" : "production"); - - if (comm::init() == -1 || hp::init() == -1) - { - deinit(); - return -1; - } - - // After initializing primary subsystems, register the exit handler. - signal(SIGINT, &sig_exit_handler); - signal(SIGTERM, &sig_exit_handler); - - // Waiting for the websocket sessions. - comm::wait(); - deinit(); - - LOG_INFO << "sashimono agent exited normally."; + return -1; } - else if (conf::ctx.command == "version") - LOG_INFO << "Sashimono Agent " << conf::cfg.version; - return 0; + // After initializing primary subsystems, register the exit handler. + signal(SIGINT, &sig_exit_handler); + signal(SIGTERM, &sig_exit_handler); + + // Waiting for the websocket sessions. + comm::wait(); + + deinit(); + + LOG_INFO << "sashimono agent exited normally."; } + + return 0; } \ No newline at end of file diff --git a/src/pchheader.hpp b/src/pchheader.hpp index 65b1e5b..0a87404 100644 --- a/src/pchheader.hpp +++ b/src/pchheader.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/salog.cpp b/src/salog.cpp index e1e9f8f..8eae775 100644 --- a/src/salog.cpp +++ b/src/salog.cpp @@ -72,7 +72,7 @@ namespace salog const std::string trace_file = conf::ctx.log_dir + "/sa.log"; static plog::RollingFileAppender fileAppender(trace_file.c_str(), conf::cfg.log.max_mbytes_per_file * 1024 * 1024, conf::cfg.log.max_file_count); - static plog::ColorConsoleAppender consoleAppender; + static plog::ConsoleAppender consoleAppender; plog::Logger<0> &logger = plog::init(level); diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 0000000..eef5bc5 --- /dev/null +++ b/src/version.cpp @@ -0,0 +1,40 @@ +#include "version.hpp" + +namespace version +{ + /** + * Compare two version strings in the format of "1.12.3". + * v1 < v2 -> returns -1 + * v1 == v2 -> returns 0 + * v1 > v2 -> returns +1 + * Error -> returns -2 + * + * Remark on string_view: In other places of the code-base we utilize string_view + * to pass immutable string references around. However in this function we keep the 'const string&' + * syntax because istringstream doesn't support string_view. It's not worth optimising + * this code as it's not being used in high-scale processing. + */ + int version_compare(const std::string &x, const std::string &y) + { + std::istringstream ix(x), iy(y); + while (ix.good() || iy.good()) + { + int cx = 0, cy = 0; + ix >> cx; + iy >> cy; + + if ((!ix.eof() && !ix.good()) || (!iy.eof() && !iy.good())) + return -2; + + if (cx > cy) + return 1; + if (cx < cy) + return -1; + + ix.ignore(); + iy.ignore(); + } + + return 0; + } +} \ No newline at end of file diff --git a/src/version.hpp b/src/version.hpp new file mode 100644 index 0000000..997ec03 --- /dev/null +++ b/src/version.hpp @@ -0,0 +1,17 @@ +#ifndef _SA_VERSION_ +#define _SA_VERSION_ + +#include "pchheader.hpp" + +namespace version +{ + // Sashimono agent version. Written to new configs. + constexpr const char *AGENT_VERSION = "1.0.0"; + + // Minimum compatible config version (this will be used to validate configs). + constexpr const char *MIN_CONFIG_VERSION = "1.0.0"; + + int version_compare(const std::string &x, const std::string &y); +} + +#endif \ No newline at end of file