Ver código fonte

Merge branch 'us06-interactive-cli' into 'master'

US06: Interactive CLI

See merge request tobias.wach/ccats!6
Wach, Tobias Alexander 5 anos atrás
pai
commit
7727597a78

+ 11 - 3
cli/CMakeLists.txt

@@ -1,13 +1,21 @@
 cmake_minimum_required(VERSION 2.8)
 
 # set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set (CMAKE_BUILD_TYPE debug)
 
 project(ccats-cli)
 
-add_executable(ccats-cli src/main.cpp)
+add_executable(ccats-cli src/main.cpp src/iomanager.cpp src/machineiomanager.cpp src/useriomanager.cpp src/commands.cpp)
+
+# use pkg-config to find readline as it doesnt provide cmake files
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(READLINE readline)
+pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
 find_package(Threads)
 find_package(Boost 1.67 REQUIRED COMPONENTS system program_options)
 
-include_directories(${Boost_INCLUDE_DIR})
-target_link_libraries(ccats-cli PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
+
+include_directories(${Boost_INCLUDE_DIR} include)
+
+target_link_libraries(ccats-cli PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${READLINE_LIBRARIES} ${JSONCPP_LIBRARIES})

+ 36 - 0
cli/include/commands.hpp

@@ -0,0 +1,36 @@
+#ifndef COMMANDS_HPP
+#define COMMANDS_HPP
+
+#include <cstring>
+#include <cctype>
+
+#define COMMANDLEN 10
+#define sizeofarr(a) (sizeof(a) / sizeof(a[0]))
+
+typedef enum {
+  CMD_HELP,
+  CMD_CONNECT,
+  CMD_DISCONNECT,
+  CMD_PUT,
+  CMD_REMOVE,
+  CMD_GET,
+  CMD_STATUS,
+  CMD_SETUP,
+  CMD_LOG,
+  CMD_UNKNOWN
+} COMMANDID;
+
+typedef struct {
+  COMMANDID cmd;
+  const char *name;
+  const char *desc;
+} CMD;
+
+extern CMD commands[];
+
+COMMANDID getCmdIdFromString(const char *str);
+const char *getCmdStringFromId(COMMANDID id);
+// TODO
+void printCmds(void);
+
+#endif

+ 6 - 0
cli/include/global.hpp

@@ -0,0 +1,6 @@
+#ifndef GLOBAL_HPP
+#define GLOBAL_HPP
+
+#define VERSION "0.1"
+
+#endif

+ 29 - 0
cli/include/iomanager.hpp

@@ -0,0 +1,29 @@
+#ifndef IOMANAGER_HPP
+#define IOMANAGER_HPP
+
+#include "global.hpp"
+
+#include <string>
+#include <boost/asio.hpp>
+
+using boost::asio::ip::tcp;
+
+class IoManager {
+protected:
+	boost::asio::io_service ios;
+	tcp::socket *tcpsock;
+	boost::system::error_code errcode;
+	std::string *ipstring;
+	int port;
+public:
+	// Basic constructor
+	IoManager(char *ipcstring);
+	// destructor to clean up all generic stuff
+	~IoManager();
+	// enters loop to handle further interaction based on derived class
+	virtual void run() = 0;
+	// tries to establish connection, returns false on error
+	bool connect();
+};
+
+#endif

+ 15 - 0
cli/include/machineiomanager.hpp

@@ -0,0 +1,15 @@
+#ifndef MACHINEIOMANAGER_HPP
+#define MACHINEIOMANAGER_HPP
+
+#include "iomanager.hpp"
+
+class MachineIoManager : public IoManager {
+
+public:
+	// MachineIoManager(char *ipstring) : IoManager(ipstring) {};
+	using IoManager::IoManager;
+	// enters loop to handle machine based interaction (other frontends)
+	void run();
+};
+
+#endif

+ 15 - 0
cli/include/useriomanager.hpp

@@ -0,0 +1,15 @@
+#ifndef USERIOMANAGER_HPP
+#define USERIOMANAGER_HPP
+
+#include "iomanager.hpp"
+
+class UserIoManager : public IoManager {
+
+public:
+	// UserIoManager(char *ipstring) : IoManager(ipstring) {};
+	using IoManager::IoManager;
+	// enters loop to handle user based interaction (from the terminal)
+	void run();
+};
+
+#endif

+ 44 - 0
cli/src/commands.cpp

@@ -0,0 +1,44 @@
+#include "../include/commands.hpp"
+
+CMD
+    commands[]{
+        {CMD_HELP, "help", "show help"},
+        {CMD_STATUS, "status", "query status of IP"},
+        {CMD_DISCONNECT, "disconnect", "disconnect from IP"},
+
+        /* TODO
+        {CMD_PUT	, "put", "upload file to IP and add to queue"},
+        {CMD_REMOVE	, "remove", "remove file from IP and queue (stops xfer
+        if required)"}, {CMD_GET	, "get", "retrieve file from IP"},
+        {CMD_SETUP	, "setup", "configure server at IP"},
+        {CMD_LOG	, "log", "show log from IP"}
+        */
+    };
+
+COMMANDID getCmdIdFromString(const char *str) {
+  COMMANDID ret = CMD_UNKNOWN;
+  char temp[COMMANDLEN + 1] = {0};
+  if (std::strlen(str) > COMMANDLEN)
+    return ret;
+  for (int i = 0; i < COMMANDLEN; i++)
+    temp[i] = std::tolower(str[i]);
+
+  for (int i = 0; i < sizeofarr(commands); i++) {
+    if (!std::strncmp(temp, commands[i].name, COMMANDLEN))
+      ret = commands[i].cmd;
+  }
+  return ret;
+}
+
+const char *getCmdStringFromId(COMMANDID id) {
+  const char *ret = NULL;
+  for (int i = 0; i < sizeofarr(commands); i++) {
+    if (commands[i].cmd == id)
+      ret = commands[i].name;
+  }
+  return ret;
+}
+
+void printCmds(void) {
+  // TODO
+}

+ 224 - 0
cli/src/iomanager.cpp

@@ -0,0 +1,224 @@
+#include "../include/iomanager.hpp"
+
+#include <iostream>
+#include <jsoncpp/json/json.h>
+
+using boost::asio::buffer;
+
+IoManager::IoManager(char *ipcstring) {
+  ipstring = new std::string(ipcstring);
+  port = 1234;
+  tcpsock = new tcp::socket(ios);
+}
+
+bool IoManager::connect() {
+  boost::asio::streambuf recvbuf;
+  tcp::endpoint *ep;
+  Json::Value root, checkok;
+  const char *recvjson;
+  std::string jsonerror;
+  bool bcheckok;
+
+  Json::CharReaderBuilder rbuilder;
+  const std::unique_ptr<Json::CharReader> reader(rbuilder.newCharReader());
+  Json::StreamWriterBuilder wbuilder;
+  // builder["indentation"] = ""; // If you want whitespace-less output
+  // const std::string output = Json::writeString(wbuilder, root);
+
+  ep =
+      new tcp::endpoint(boost::asio::ip::address::from_string(*ipstring), 1234);
+
+  // establish connection
+  std::cerr << "connecting to " << *ipstring << std::endl;
+  tcpsock->connect(*ep, errcode);
+  if (errcode) {
+    std::cerr << "couldnt connect to " << *ipstring << std::endl
+              << errcode.message() << std::endl;
+    return false;
+  }
+
+  printf("connect ok\n");
+  fflush(stdout);
+
+  // send version check
+  root["version"] = VERSION;
+
+  std::cout << root << std::endl;
+
+  boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, root)),
+                     errcode);
+  if (errcode) {
+    std::cerr << "couldnt send version check" << std::endl
+              << errcode.message() << std::endl;
+    return false;
+  }
+
+  printf("send ok\n");
+  fflush(stdout);
+
+  // recieve answer to version check
+  // using transfer_at_least(1) to avoid lockup with transfer_all()
+  boost::asio::read(*tcpsock, recvbuf, boost::asio::transfer_at_least(1),
+                    errcode);
+  //~ boost::asio::read(*tcpsock, recvbuf, errcode);
+  if (errcode && errcode != boost::asio::error::eof) {
+    std::cerr << "couldnt recieve version check" << std::endl
+              << errcode.message() << std::endl;
+    return false;
+  }
+
+  printf("recieve ok\n");
+  fflush(stdout);
+
+  // parse json
+  recvjson = boost::asio::buffer_cast<const char *>(recvbuf.data());
+  if (!reader->parse(recvjson, recvjson + recvbuf.size(), &root, &jsonerror)) {
+    std::cerr << "couldnt parse recieved json" << std::endl
+              << jsonerror << std::endl;
+    return false;
+  }
+
+  printf("parse ok\n");
+  fflush(stdout);
+
+  // remove processed data from recvbuf
+  recvbuf.consume(recvbuf.size());
+
+  std::cout << root << std::endl;
+
+  // check if version check was ok
+  checkok = root["accept"];
+
+  if (!checkok.asBool()) {
+    std::cerr << "version check failed. client version is " << VERSION
+              << std::endl
+              << "server reports version " << root["version"] << std::endl;
+    return false;
+  }
+
+  printf("check ok\n\n\n");
+  fflush(stdout);
+
+  /* */
+  // TODO remove hardcoded login
+  root = Json::Value();
+  // send version check
+  // TODO make client version global
+  root["user"] = "user";
+  root["pass"] = "pass";
+
+  std::cout << root << std::endl;
+
+  boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, root)),
+                     errcode);
+  if (errcode) {
+    std::cerr << "couldnt send login" << std::endl
+              << errcode.message() << std::endl;
+    return false;
+  }
+
+  printf("send ok\n");
+  fflush(stdout);
+
+  // recieve answer to version check
+  // using transfer_at_least(1) to avoid lockup with transfer_all()
+  boost::asio::read(*tcpsock, recvbuf, boost::asio::transfer_at_least(1),
+                    errcode);
+  //~ boost::asio::read(*tcpsock, recvbuf, errcode);
+  if (errcode && errcode != boost::asio::error::eof) {
+    std::cerr << "couldnt recieve login response" << std::endl
+              << errcode.message() << std::endl;
+    return false;
+  }
+
+  printf("recieve ok\n");
+  fflush(stdout);
+
+  // parse json
+  recvjson = boost::asio::buffer_cast<const char *>(recvbuf.data());
+  if (!reader->parse(recvjson, recvjson + recvbuf.size(), &root, &jsonerror)) {
+    std::cerr << "couldnt parse recieved json" << std::endl
+              << jsonerror << std::endl;
+    return false;
+  }
+
+  printf("parse ok\n");
+  fflush(stdout);
+
+  // remove processed data from recvbuf
+  recvbuf.consume(recvbuf.size());
+
+  std::cout << root << std::endl;
+  /* */
+
+  // clean up
+  delete ep;
+
+  return true;
+}
+
+IoManager::~IoManager() {
+
+  /* */
+
+  boost::asio::streambuf recvbuf;
+  Json::Value root, checkok;
+  const char *recvjson;
+  std::string jsonerror;
+
+  Json::CharReaderBuilder rbuilder;
+  const std::unique_ptr<Json::CharReader> reader(rbuilder.newCharReader());
+  Json::StreamWriterBuilder wbuilder;
+
+  // TODO remove hardcoded login
+  root = Json::Value();
+  // send version check
+  // TODO make client version global
+  root["command"] = "close";
+
+  std::cout << root << std::endl;
+
+  boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, root)),
+                     errcode);
+  if (errcode) {
+    std::cerr << "couldnt send close" << std::endl
+              << errcode.message() << std::endl;
+  }
+
+  printf("send ok\n");
+  fflush(stdout);
+
+  // recieve answer to version check
+  // using transfer_at_least(1) to avoid lockup with transfer_all()
+  boost::asio::read(*tcpsock, recvbuf, boost::asio::transfer_at_least(1),
+                    errcode);
+  //~ boost::asio::read(*tcpsock, recvbuf, errcode);
+  if (errcode && errcode != boost::asio::error::eof) {
+    std::cerr << "couldnt recieve close response" << std::endl
+              << errcode.message() << std::endl;
+  }
+
+  printf("recieve ok\n");
+  fflush(stdout);
+
+  // parse json
+  recvjson = boost::asio::buffer_cast<const char *>(recvbuf.data());
+  if (!reader->parse(recvjson, recvjson + recvbuf.size(), &root, &jsonerror)) {
+    std::cerr << "couldnt parse recieved json" << std::endl
+              << jsonerror << std::endl;
+  }
+
+  printf("parse ok\n");
+  fflush(stdout);
+
+  // remove processed data from recvbuf
+  recvbuf.consume(recvbuf.size());
+
+  std::cout << root << std::endl;
+  /* */
+
+  tcpsock->close();
+
+  delete ipstring;
+  delete tcpsock;
+}

+ 83 - 0
cli/src/machineiomanager.cpp

@@ -0,0 +1,83 @@
+#include "../include/machineiomanager.hpp"
+#include "../include/commands.hpp"
+
+#include <iostream>
+#include <jsoncpp/json/json.h>
+
+#include <readline/readline.h>
+
+using boost::asio::buffer;
+
+void MachineIoManager::run() {
+  std::cout << "MachineIoManager::run() says hello!" << std::endl;
+
+  boost::asio::streambuf recvbuf;
+  boost::system::error_code errcode;
+
+  Json::Value root;
+  const char *recvjson;
+
+  Json::StreamWriterBuilder wbuilder;
+
+  bool keep_reading = true;
+  COMMANDID cmd;
+  char *line = NULL;
+  while (keep_reading) {
+    free(line);
+    root = Json::Value();
+
+    // read an input line
+    line = readline("");
+
+    // if no input continue
+    if (strlen(line) == 0) {
+      continue;
+    }
+
+    cmd = getCmdIdFromString(line);
+
+    switch (cmd) {
+    case CMD_DISCONNECT: {
+      std::cerr << "disconnecting\n";
+      keep_reading = false;
+      break;
+    }
+    case CMD_STATUS: {
+      root["command"] = "status";
+
+      // send request
+      boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, root)),
+                         errcode);
+      if (errcode) {
+        std::cerr << "couldnt send version check" << std::endl
+                  << errcode.message() << std::endl;
+        break;
+      }
+
+      // receive answer
+      // using transfer_at_least(1) to avoid lockup with transfer_all()
+      boost::asio::read(*tcpsock, recvbuf, boost::asio::transfer_at_least(1),
+                        errcode);
+      if (errcode && errcode != boost::asio::error::eof) {
+        std::cerr << "couldnt recieve version check" << std::endl
+                  << errcode.message() << std::endl;
+        break;
+      }
+
+      // dump received data to stdout for gui to handle
+      recvjson = boost::asio::buffer_cast<const char *>(recvbuf.data());
+      std::cout << recvjson;
+
+      break;
+    }
+    default: {
+      std::cerr << "unknown command " << line << "\n";
+      break;
+    }
+    }
+    std::printf("\n");
+  }
+  free(line);
+
+  return;
+}

+ 43 - 134
cli/src/main.cpp

@@ -1,170 +1,79 @@
+#include "../include/commands.hpp"
+#include "../include/machineiomanager.hpp"
+#include "../include/useriomanager.hpp"
+
 #include <boost/asio.hpp>
 #include <boost/program_options.hpp>
-#include <cctype>
 #include <iostream>
-
-#define COMMANDLEN 10
-#define sizeofarr(a) (sizeof(a) / sizeof(a[0]))
+#include <string>
 
 namespace bpo = boost::program_options;
-namespace bai = boost::asio;
-using bai::ip::tcp;
-
-typedef enum {
-  CMD_HELP,
-  CMD_CONNECT,
-  CMD_DISCONNECT,
-  CMD_PUT,
-  CMD_REMOVE,
-  CMD_GET,
-  CMD_QUERY,
-  CMD_SETUP,
-  CMD_LOG,
-  CMD_UNKNOWN
-} COMMANDTYPES;
-
-typedef struct {
-  COMMANDTYPES cmd;
-  const char *name;
-  const char *desc;
-} CMD;
-
-CMD commands[]{{CMD_HELP, "help", "show help"},
-               {CMD_CONNECT, "connect", "connect to IP"},
-               {CMD_DISCONNECT, "disconnect", "disconnect from IP"},
-               {CMD_PUT, "put", "upload file to IP and add to queue"},
-               {CMD_REMOVE, "remove",
-                "remove file from IP and queue (stops xfer if required)"},
-               {CMD_GET, "get", "retrieve file from IP"},
-               {CMD_QUERY, "query", "query status of IP"},
-               {CMD_SETUP, "setup", "configure server at IP"},
-               {CMD_LOG, "log", "show log from IP"}};
-
-/*  takes a string in str and checks if it is a valid command
-    returns the matching COMMANDTYPES or CMD_UNKNOWN
-    if isshort is nonzero, only the first character of str will be checked
-*/
-COMMANDTYPES getCommand(char *str, unsigned int isshort) {
-  COMMANDTYPES ret = CMD_UNKNOWN;
-  char temp[COMMANDLEN + 1] = {0};
-  if (strlen(str) > COMMANDLEN)
-    return ret;
-  if (isshort)
-    temp[0] = std::tolower(str[0]);
-  else
-    for (int i = 0; i < COMMANDLEN; i++)
-      temp[i] = std::tolower(str[i]);
-
-  for (int i = 0; i < sizeofarr(commands); i++) {
-    if (isshort) {
-      if (tolower(str[0]) == commands[i].name[0])
-        ret = commands[i].cmd;
-    } else {
-      if (!strncmp(temp, commands[i].name, COMMANDLEN))
-        ret = commands[i].cmd;
-    }
-  }
-  return ret;
-}
 
 void show_help(char *exec) {
   std::printf("ccats command line interface\n");
-  std::printf("usage: %s COMMAND IP [Options]\n", exec);
-  std::printf("available COMMANDs are:\n");
-  for (int i = 0; i < sizeofarr(commands); i++) {
-    std::printf("%10s - %s\n", commands[i].name, commands[i].desc);
-  }
+  std::printf("usage: %s IP [Options]\n", exec);
   std::printf("IP should be in the format \"xxx.xxx.xxx.xxx\"\n");
 }
 
 int main(int argc, char **argv) {
   bpo::options_description desc{"Options"};
-  desc.add_options()("cooloption", "Cooloption to use with command FOON");
+  desc.add_options()("help", "show this help")(
+      "machine",
+      "switch to machine mode for I/O (not designed for user interaction)")(
+      "batch", bpo::value<std::string>(),
+      "run operations from arg as batch file");
+
   bpo::variables_map vm;
-  bai::io_service ios;
-  tcp::socket socket(ios);
-  boost::system::error_code err;
-  bai::streambuf recvbuf;
+  unsigned int machine = 0;
+  const char *file = NULL;
+  IoManager *ioman;
 
-  // handle no command
-  if (argc < 2) {
+  if (argc < 2 || !std::strncmp(argv[1], "--help", 6)) {
     show_help(argv[0]);
-    exit(1);
+    std::cout << desc;
+    return 1;
   }
 
-  // identify command, print error message if unknown
-  COMMANDTYPES cmd = getCommand(argv[1], (strlen(argv[1]) == 1));
-  switch (cmd) {
-  case CMD_UNKNOWN:
-    std::printf("unknown command\n");
-  case CMD_HELP:
+  if (!isdigit(argv[1][0])) {
+    std::printf("invalid ip\n");
     show_help(argv[0]);
     std::cout << desc;
-    exit(1);
-  }
-  if (argc < 3) {
-    std::printf("not enough arguments\n");
-    show_help(argv[0]);
-    exit(1);
+    return 1;
   }
 
-  // parse additional arguments, possibly drop this in the future for full
-  // interactivity
   try {
-    store(parse_command_line(argc - 2, argv + 2, desc), vm);
+    store(parse_command_line(argc - 1, argv + 1, desc), vm);
     notify(vm);
 
     if (vm.count("help")) {
+      show_help(argv[0]);
       std::cout << desc;
-    } else {
-      std::printf("no additional options\n");
+      return 1;
+    }
+    if (vm.count("machine")) {
+      // enable machine/gui mode
+      machine = 1;
+    }
+    if (vm.count("batch")) {
+      // handle batch file mode
+      file = vm["batch"].as<std::string>().c_str();
     }
   } catch (const bpo::error &ex) {
     std::fprintf(stderr, "%s\n", ex.what());
   }
+  std::printf("ip %s machine mode is %d file is %s\n", argv[1], machine,
+              file ? file : "");
 
-  // have enough valid arguments, work through them
-  switch (cmd) {
-  case CMD_CONNECT:
-    std::printf("connecting to %s\n", argv[2]);
-    socket.connect(tcp::endpoint(bai::ip::address::from_string(argv[2]), 1234));
-    std::printf("connected to %s - sending hello\n", argv[2]);
-    bai::write(socket, bai::buffer("Client CONNECT test\n"), err);
-    if (!err) {
-      std::printf("OK: sent message\n");
-    } else {
-      std::printf("ERR: couldnt send message: %s\n", err.message().c_str());
-    }
-    bai::read(socket, recvbuf, bai::transfer_all(), err);
-    if (err && err != bai::error::eof) {
-      std::printf("ERR: couldnt recieve message: %s\n", err.message().c_str());
-    } else {
-      std::printf("OK: recieved message %s\n",
-                  bai::buffer_cast<const char *>(recvbuf.data()));
-    }
-    break;
-  case CMD_DISCONNECT:
-    std::printf("disconnecting from %s\n", argv[2]);
-    socket.connect(tcp::endpoint(bai::ip::address::from_string(argv[2]), 1234));
-    std::printf("connected to %s - sending goodbye\n", argv[2]);
-    bai::write(socket, bai::buffer("Client DISCONNECT test\n"), err);
-    if (!err) {
-      std::printf("OK: sent message\n");
-    } else {
-      std::printf("ERR: couldnt send message: %s\n", err.message().c_str());
-    }
-    bai::read(socket, recvbuf, bai::transfer_all(), err);
-    if (err && err != bai::error::eof) {
-      std::printf("ERR: couldnt recieve message: %s\n", err.message().c_str());
-    } else {
-      std::printf("OK: recieved message %s\n",
-                  bai::buffer_cast<const char *>(recvbuf.data()));
-    }
-    break;
-  default:
-    std::printf("command %s not implemented\n", argv[1]);
-    break;
+  if (machine) {
+    ioman = new MachineIoManager(argv[1]);
+  } else {
+    ioman = new UserIoManager(argv[1]);
   }
 
+  // ‘MachineIoManager::MachineIoManager(char*&)’
+  if (ioman->connect()) {
+    ioman->run();
+  }
+  delete ioman;
   std::printf("done\n");
 }

+ 121 - 0
cli/src/useriomanager.cpp

@@ -0,0 +1,121 @@
+#include "../include/useriomanager.hpp"
+#include "../include/commands.hpp"
+
+#include <iostream>
+#include <jsoncpp/json/json.h>
+
+#include <readline/history.h>
+#include <readline/readline.h>
+
+using boost::asio::buffer;
+
+void UserIoManager::run() {
+  std::cout << "UserIoManager::run() says hello!" << std::endl;
+
+  bool keep_reading = true;
+  COMMANDID cmd;
+  char *line = NULL;
+  while (keep_reading) {
+    free(line);
+
+    // read an input line
+    line = readline("ccats> ");
+
+    // if no input, do not add to history, and go to reading next line
+    if (strlen(line) == 0) {
+      continue;
+    }
+
+    // add the line to history
+    add_history(line);
+
+    cmd = getCmdIdFromString(line);
+
+    switch (cmd) {
+    case CMD_STATUS: {
+
+      boost::asio::streambuf recvbuf;
+      Json::Value root, checkok;
+      const char *recvjson;
+      std::string jsonerror;
+
+      Json::CharReaderBuilder rbuilder;
+      const std::unique_ptr<Json::CharReader> reader(rbuilder.newCharReader());
+      Json::StreamWriterBuilder wbuilder;
+
+      // ask for status
+      root["command"] = "status";
+      boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, root)),
+                         errcode);
+      if (errcode) {
+        std::cerr << "couldnt send status query to server " << ipstring
+                  << std::endl
+                  << errcode.message() << std::endl;
+        keep_reading = false;
+        continue;
+      }
+
+      // recieve answer to status query
+      boost::asio::read(*tcpsock, recvbuf, boost::asio::transfer_at_least(1),
+                        errcode);
+      if (errcode && errcode != boost::asio::error::eof) {
+        std::cerr << "couldnt recieve status from " << ipstring << std::endl
+                  << errcode.message() << std::endl;
+        keep_reading = false;
+        continue;
+      }
+
+      // parse json
+      recvjson = boost::asio::buffer_cast<const char *>(recvbuf.data());
+      if (!reader->parse(recvjson, recvjson + recvbuf.size(), &root,
+                         &jsonerror)) {
+        std::cerr << "couldnt parse recieved json" << std::endl
+                  << jsonerror << std::endl;
+        keep_reading = false;
+        continue;
+      }
+
+      // remove processed data from recvbuf
+      recvbuf.consume(recvbuf.size());
+
+      // check if received answer to right query
+      checkok = root["command"];
+
+      if (checkok.asString().compare("status") != 0) {
+        std::cerr << "command check failed. client sent command \"status\""
+                  << std::endl
+                  << "server replied to command \"" << checkok << "\""
+                  << std::endl;
+        keep_reading = false;
+        continue;
+      } else {
+        std::cout << "server replied with status: " << root["response"]
+                  << std::endl;
+      }
+
+      break;
+    }
+    case CMD_DISCONNECT: {
+      std::printf("disconnecting\n");
+      keep_reading = false;
+      break;
+    }
+    default: {
+      std::printf("unknown command %s\n", line);
+    }
+    case CMD_HELP: {
+      std::printf("\n");
+      std::printf("AVAILABLE COMMANDS are:\n");
+      // printCmds();
+      std::printf("help        - shows this help\n");
+      std::printf("status      - asks the server for status\n");
+      std::printf("disconnect  - closes the connection to the server\n");
+      break;
+    }
+    }
+    std::printf("\n");
+  }
+  free(line);
+
+  return;
+}