diff --git a/tools/libstorage-cpp/storage_lib.cpp b/tools/libstorage-cpp/storage_lib.cpp index 36108619..2ef36573 100644 --- a/tools/libstorage-cpp/storage_lib.cpp +++ b/tools/libstorage-cpp/storage_lib.cpp @@ -33,6 +33,37 @@ struct Options { std::atomic g_stop{false}; int g_serverFd = -1; +enum class NodeState { + Running, + Stopped, + Closed, +}; + +std::string stateName(NodeState state) { + switch (state) { + case NodeState::Running: return "RUNNING"; + case NodeState::Stopped: return "STOPPED"; + case NodeState::Closed: return "CLOSED"; + } + return "UNKNOWN"; +} + +std::string availableCommands(NodeState state) { + switch (state) { + case NodeState::Running: + return "all storage commands, stop, shutdown, destroy"; + case NodeState::Stopped: + return "start, close, shutdown, destroy"; + case NodeState::Closed: + return "shutdown, destroy"; + } + return "shutdown, destroy"; +} + +std::string unavailableForState(NodeState state) { + return "node is " + stateName(state) + "; available commands: " + availableCommands(state); +} + std::string homeDir() { const char* home = std::getenv("HOME"); if (!home || std::strlen(home) == 0) { @@ -176,13 +207,56 @@ std::string infoResult(StorageClient& client) { "\"peer_id\":\"" + jsonEscape(client.peerId()) + "\"}"; } -std::string dispatch(StorageClient& client, const Options& options, const std::string& line) { +std::string dispatch( + StorageClient& client, + const Options& options, + NodeState& state, + const std::string& line) { const auto parts = splitLine(line); if (parts.empty()) { throw std::runtime_error("empty command"); } const auto& cmd = parts[0]; + + if (cmd == "shutdown" || cmd == "destroy") { + g_stop = true; + return "shutting down"; + } + + if (state == NodeState::Closed) { + throw std::runtime_error(unavailableForState(state)); + } + + if (cmd == "start") { + if (state == NodeState::Running) return "node is already started"; + client.start(); + state = NodeState::Running; + return "started"; + } + + if (cmd == "stop") { + if (state == NodeState::Stopped) { + return "node is not started; available commands: " + availableCommands(state); + } + client.stop(); + state = NodeState::Stopped; + return "stopped"; + } + + if (cmd == "close") { + if (state == NodeState::Running) { + throw std::runtime_error("node is RUNNING; call stop before close"); + } + client.close(); + state = NodeState::Closed; + return "closed"; + } + + if (state == NodeState::Stopped) { + throw std::runtime_error(unavailableForState(state)); + } + if (cmd == "info") return infoResult(client); if (cmd == "version") return client.version(); if (cmd == "revision") return client.revision(); @@ -233,11 +307,6 @@ std::string dispatch(StorageClient& client, const Options& options, const std::s std::vector addresses(parts.begin() + 2, parts.end()); return client.connect(parts[1], addresses); } - if (cmd == "shutdown") { - g_stop = true; - return "shutting down"; - } - throw std::runtime_error("unknown command: " + cmd); } @@ -310,6 +379,7 @@ int main(int argc, char** argv) { StorageClient client(configJson(options), options.timeout); client.start(); + NodeState state = NodeState::Running; g_serverFd = createServerSocket(options.socketPath); std::cout << "storage_lib listening on " << options.socketPath << "\n"; @@ -325,7 +395,7 @@ int main(int argc, char** argv) { try { const std::string line = readLine(clientFd); - writeAll(clientFd, okResponse(dispatch(client, options, line))); + writeAll(clientFd, okResponse(dispatch(client, options, state, line))); } catch (const std::exception& err) { writeAll(clientFd, errorResponse(err.what())); } diff --git a/tools/libstorage-cpp/storage_lib_ctl.cpp b/tools/libstorage-cpp/storage_lib_ctl.cpp index 05c895a5..92d67480 100644 --- a/tools/libstorage-cpp/storage_lib_ctl.cpp +++ b/tools/libstorage-cpp/storage_lib_ctl.cpp @@ -35,7 +35,11 @@ void printUsage() { " storage_lib_ctl info\n" " storage_lib_ctl upload README.md\n" " storage_lib_ctl download ./out true\n" - " storage_lib_ctl shutdown\n"; + " storage_lib_ctl stop\n" + " storage_lib_ctl start\n" + " storage_lib_ctl close\n" + " storage_lib_ctl shutdown\n" + " storage_lib_ctl destroy\n"; } struct Options { diff --git a/tools/storage-test/storage-test.sh b/tools/storage-test/storage-test.sh index d7ecd161..01522950 100755 --- a/tools/storage-test/storage-test.sh +++ b/tools/storage-test/storage-test.sh @@ -68,6 +68,7 @@ Target commands: Lib-only target commands: lib spr lib debug + lib peers lib manifest lib connect [addr...] @@ -453,10 +454,18 @@ lib_only_command() { local command="$1" shift case "$command" in - spr|debug) + spr) [[ $# -eq 0 ]] || die "$command does not accept arguments" lib_result "$command" ;; + debug) + [[ $# -eq 0 ]] || die 'debug does not accept arguments' + lib_result debug | jq . + ;; + peers) + [[ $# -eq 0 ]] || die 'peers does not accept arguments' + lib_result debug | jq -r '.table.nodes | length' + ;; manifest) [[ $# -eq 1 ]] || die 'manifest requires ' lib_result manifest "$1" @@ -785,7 +794,7 @@ target_command() { space) [[ $# -eq 0 ]] || die 'space does not accept arguments'; target_simple_get "$target" 'space' ;; peerid) [[ $# -eq 0 ]] || die 'peerid does not accept arguments'; target_simple_get "$target" 'peerid' ;; test) [[ $# -eq 0 ]] || die 'test does not accept arguments yet'; target_test "$target" ;; - spr|debug|manifest|connect) + spr|debug|peers|manifest|connect) [[ "$target" == 'lib' ]] || die "$command is currently supported only for lib target" lib_only_command "$command" "$@" ;;