From 0e8a66989062cc409187f0ce0c4c2e3ae6e4c7ae Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 1 Feb 2015 13:04:15 +0100 Subject: [PATCH] added command help, nicer implementation of quit and stop commands, nicer help --- src/ebusd/mainloop.cpp | 181 ++++++++++++++++++++++++++++------------- src/ebusd/mainloop.h | 42 +++++++--- src/ebusd/network.cpp | 11 ++- src/ebusd/network.h | 36 +++++--- 4 files changed, 184 insertions(+), 86 deletions(-) diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index fad44d97..8a6f19a2 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -41,11 +41,11 @@ MainLoop::MainLoop(const struct options opt, DataFieldTemplates* templates, Mess m_address, opt.answer, opt.acquireRetries, opt.sendRetries, opt.acquireTimeout, opt.receiveTimeout, - opt.numberMasters, opt.pollInterval); + opt.masterCount, opt.pollInterval); m_busHandler->start("bushandler"); // create network - m_network = new Network(opt.localhost, opt.port, &m_netQueue); + m_network = new Network(opt.localOnly, opt.port, &m_netQueue); m_network->start("network"); } @@ -74,10 +74,12 @@ MainLoop::~MainLoop() void MainLoop::run() { - for (;;) { + bool running = true; + + while (running == true) { string result; - // recv new message from client + // pick the next message to handle NetMessage* message = m_netQueue.remove(); string data = message->getData(); @@ -87,17 +89,13 @@ void MainLoop::run() if (listening == false) since = until; + bool connected = true; if (data.length() > 0) { data.erase(remove(data.begin(), data.end(), '\r'), data.end()); data.erase(remove(data.begin(), data.end(), '\n'), data.end()); logNotice(lf_main, ">>> %s", data.c_str()); - - // decode message - if (strcasecmp(data.c_str(), "STOP") != 0) - result = decodeMessage(data, listening); - else - result = "done"; + result = decodeMessage(data, connected, listening, running); logNotice(lf_main, "<<< %s", result.c_str()); result += "\n\n"; @@ -107,11 +105,7 @@ void MainLoop::run() } // send result to client - message->setResult(result, listening, until); - - // stop daemon - if (strcasecmp(data.c_str(), "STOP") == 0) - return; + message->setResult(result, listening, until, connected == false); } } @@ -122,7 +116,7 @@ void MainLoop::logRaw(const unsigned char byte, bool received) { logNotice(lf_bus, ">%02x", byte); } -string MainLoop::decodeMessage(const string& data, bool& listening) +string MainLoop::decodeMessage(const string& data, bool& connected, bool& listening, bool& running) { ostringstream result; @@ -157,8 +151,16 @@ string MainLoop::decodeMessage(const string& data, bool& listening) if (args.size() == 0) return "command missing"; - const char* str = args[0].c_str(); + if (args.size() == 2) { + // check for "CMD -h" + if (strcasecmp(args[1].c_str(), "-h") == 0 || strcasecmp(args[1].c_str(), "-?") == 0 || strcasecmp(args[1].c_str(), "--help") == 0) + args.clear(); // empty args is used as command help indicator + else if (strcasecmp(args[0].c_str(), "H") == 0 || strcasecmp(args[0].c_str(), "HELP") == 0) { // check for "HELP CMD" + str = args[1].c_str(); + args.clear(); // empty args is used as command help indicator + } + } if (strcasecmp(str, "R") == 0 || strcasecmp(str, "READ") == 0) return executeRead(args); if (strcasecmp(str, "W") == 0 || strcasecmp(str, "WRITE") == 0) @@ -168,7 +170,7 @@ string MainLoop::decodeMessage(const string& data, bool& listening) if (strcasecmp(str, "L") == 0 || strcasecmp(str, "LISTEN") == 0) return executeListen(args, listening); if (strcasecmp(str, "STATE") == 0) - return executeState(); + return executeState(args); if (strcasecmp(str, "SCAN") == 0) return executeScan(args); if (strcasecmp(str, "LOG") == 0) @@ -179,6 +181,10 @@ string MainLoop::decodeMessage(const string& data, bool& listening) return executeDump(args); if (strcasecmp(str, "RELOAD") == 0) return executeReload(args); + if (strcasecmp(str, "STOP") == 0) + return executeStop(args, running); + if (strcasecmp(str, "Q") == 0 || strcasecmp(str, "QUIT") == 0) + return executeQuit(args, connected); if (strcasecmp(str, "H") == 0 || strcasecmp(str, "HELP") == 0) return executeHelp(); @@ -228,7 +234,14 @@ string MainLoop::executeRead(vector &args) argPos++; } if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2) - return "usage: 'read [-v] [-f] [-m seconds] [-c class] name [field]'"; + return "usage: 'read [-v] [-f] [-m SECONDS] [-c CLASS] NAME [FIELD]'\n" + " Read value(s).\n" + " -v be verbose (include field names, units, and comments)\n" + " -f force reading from the bus (same as '-m 0')\n" + " -m SECONDS only return cached value if age is less than SECONDS [300]\n" + " -c CLASS limit to messages of CLASS\n" + " NAME the NAME of the message to send\n" + " FIELD only retrieve the single FIELD"; if (args.size() == argPos + 2) maxAge = 0; // force refresh to filter single field @@ -289,12 +302,13 @@ string MainLoop::executeRead(vector &args) string MainLoop::executeWrite(vector &args) { size_t argPos = 1; - if (args.size() > argPos && args[argPos] == "-h") { + while (args.size() > argPos && args[argPos] == "-h") { argPos++; - if (args.size() < argPos + 1) - return "usage: 'write -h ZZPBSBNNDx'"; - + if (args.size() < argPos + 1) { + argPos = 0; + break; + } ostringstream msg; msg << hex << setw(2) << setfill('0') << static_cast(m_address) << setw(0); while (argPos < args.size()) { @@ -323,8 +337,18 @@ string MainLoop::executeWrite(vector &args) return getResultCode(ret); } - if (args.size() != argPos + 3) - return "usage: 'write class name value[;value]*' or 'write -h ZZPBSBNNDx'"; + if (argPos == 0 || args.size() != argPos + 3) + return "usage: 'write CLASS NAME VALUE[;VALUE]*'\n" + " or: 'write -h ZZPBSBNNDx' or 'write -h ZZ PB SB NN Dx'\n" + " Write value(s).\n" + " CLASS the CLASS of the message to send\n" + " NAME the NAME of the message to send\n" + " VALUE a single field VALUE\n" + " -h directly write hex message:\n" + " ZZ destination address\n" + " PB SB primary/secondary command byte\n" + " NN number of data bytes to send\n" + " Dx the data byte(s) to send"; Message* message = m_messages->find(args[argPos], args[argPos + 1], true); @@ -407,7 +431,13 @@ string MainLoop::executeFind(vector &args) argPos++; } if (argPos == 0 || args.size() < argPos || args.size() > argPos + 1) - return "usage: 'find [-v] [-r] [-w] [-p] [-d] [-c class] [name]'"; + return "usage: 'find [-v] [-r] [-w] [-p] [-d] [-c CLASS] [NAME]'\n" + " Find value(s)." + " -v be verbose (include field names, units, and comments)\n" + " -r limit to active read messages (default all types)\n" + " -w limit to active write messages (default all types)\n" + " -p limit to passive messages (default all types)\n" + " -d only retrieve messages with actual data"; deque messages; if (args.size() == argPos) @@ -454,7 +484,7 @@ string MainLoop::executeFind(vector &args) string MainLoop::executeListen(vector &args, bool& listening) { - if (args.size() <= 1) { + if (args.size() == 1) { if (listening == true) return "listen continued"; @@ -463,14 +493,19 @@ string MainLoop::executeListen(vector &args, bool& listening) } if (args.size() != 2 || args[1] != "stop") - return "usage: 'listen [stop]'"; + return "usage: 'listen [stop]'\n" + " Listen for updates (or stop it)."; listening = false; return "listen stopped"; } -string MainLoop::executeState() +string MainLoop::executeState(vector &args) { + if (args.size() == 0) + return "usage: 'state'\n" + " Report bus state."; + if (m_busHandler->hasSignal() == true) return "signal acquired"; @@ -479,7 +514,7 @@ string MainLoop::executeState() string MainLoop::executeScan(vector &args) { - if (args.size() <= 1) { + if (args.size() == 1) { result_t result = m_busHandler->startScan(); if (result == RESULT_OK) return "scan initiated"; @@ -488,7 +523,7 @@ string MainLoop::executeScan(vector &args) return getResultCode(result); } - if (strcasecmp(args[1].c_str(), "FULL") == 0) { + if (args.size() == 2 && strcasecmp(args[1].c_str(), "FULL") == 0) { result_t result = m_busHandler->startScan(true); if (result == RESULT_OK) return "done"; @@ -497,27 +532,31 @@ string MainLoop::executeScan(vector &args) return getResultCode(result); } - if (strcasecmp(args[1].c_str(), "RESULT") == 0) { + if (args.size() == 2 && strcasecmp(args[1].c_str(), "RESULT") == 0) { ostringstream result; m_busHandler->formatScanResult(result); return result.str(); } return "usage: 'scan'\n" - " 'scan full'\n" - " 'scan result'"; + " or: 'scan full'\n" + " or: 'scan result'\n" + " Scan seen or all slaves, or report scan result."; } string MainLoop::executeLog(vector &args) { bool result; - if (args.size() == 3 && strcasecmp(args[1].c_str(), "AREAS") == 0) - result = setLogFacilities(args[2].c_str()); + if ((args.size() == 3 || args.size() == 2) && strcasecmp(args[1].c_str(), "AREAS") == 0) + result = setLogFacilities(args.size() == 3 ? args[2].c_str() : ""); else if (args.size() == 3 && strcasecmp(args[1].c_str(), "LEVEL") == 0) result = setLogLevel(args[2].c_str()); else - return "usage: 'log areas area,area,..' (area: main|network|bus|update|all)\n" - " 'log level level' (level: error|notice|info|debug)"; + return "usage: 'log areas AREA,AREA,...'\n" + " or: 'log level LEVEL'\n" + " Set log area(s) or log level.\n" + " AREA the log area(s) to include (main|network|bus|update|all)\n" + " LEVEL the log level to set (error|notice|info|debug)"; if (result == true) return "done"; @@ -528,7 +567,8 @@ string MainLoop::executeLog(vector &args) string MainLoop::executeRaw(vector &args) { if (args.size() != 1) - return "usage: 'raw'"; + return "usage: 'raw'\n" + " Toggle log raw data."; bool enabled = !m_port->getLogRaw(); m_port->setLogRaw(enabled); @@ -539,7 +579,8 @@ string MainLoop::executeRaw(vector &args) string MainLoop::executeDump(vector &args) { if (args.size() != 1) - return "usage: 'dump'"; + return "usage: 'dump'\n" + " Toggle raw dump."; bool enabled = !m_port->getDumpRaw(); m_port->setDumpRaw(enabled); @@ -550,7 +591,8 @@ string MainLoop::executeDump(vector &args) string MainLoop::executeReload(vector &args) { if (args.size() != 1) - return "usage: 'reload'"; + return "usage: 'reload'\n" + " Reload config files."; // reload commands result_t result = loadConfigFiles(m_templates, m_messages); @@ -560,25 +602,48 @@ string MainLoop::executeReload(vector &args) return getResultCode(result); } +string MainLoop::executeStop(vector &args, bool& running) +{ + if (args.size() == 1) { + running = false; + return "daemon stopped"; + } + + return "usage: 'stop'\n" + " Stop the daemon."; +} + +string MainLoop::executeQuit(vector &args, bool& connected) +{ + if (args.size() == 1) { + connected = false; + return "connection closed"; + } + + return "usage: 'quit'\n" + " Close client connection."; +} + string MainLoop::executeHelp() { - return "commands:\n" - " read read value(s) 'read [-v] [-f] [-m seconds] [-c class] name [field]'\n" - " write write value(s) 'write class name value[;value]*' or 'write -h ZZPBSBNNDx'\n" - " find find value(s) 'find [-v] [-r] [-w] [-p] [-d] [-c class] [name]'\n" - " listen listen for updates 'listen [stop]'\n" - " state report bus state 'state'\n" - " scan scan seen slaves 'scan'\n" - " scan all slaves 'scan full'\n" - " show scan results 'scan result'\n" - " log set log areas 'log areas area,area,..' (area: main|network|bus|update|all)\n" - " set log level 'log level level' (level: error|notice|info|debug)\n" - " raw toggle log raw data 'raw'\n" - " dump toggle dump state 'dump'\n" - " reload reload config files 'reload'\n" - " stop stop daemon 'stop'\n" - " quit close connection 'quit'\n" - " help print this page 'help'"; + return "usage:\n" + " read Read value(s) 'read [-v] [-f] [-m SECONDS] [-c CLASS] NAME [FIELD]'\n" + " write Write value(s) 'write CLASS NAME VALUE[;VALUE]*' or 'write -h ZZPBSBNNDx'\n" + " find Find value(s) 'find [-v] [-r] [-w] [-p] [-d] [-c CLASS] [NAME]'\n" + " listen Listen for updates 'listen [stop]'\n" + " state Report bus state 'state'\n" + " scan Scan seen slaves 'scan'\n" + " Scan all slaves 'scan full'\n" + " Report scan result 'scan result'\n" + " log Set log areas 'log areas AREA,AREA,...' (AREA: main|network|bus|update|all)\n" + " Set log level 'log level LEVEL' (LEVEL: error|notice|info|debug)\n" + " raw Toggle log raw data 'raw'\n" + " dump Toggle raw dump 'dump'\n" + " reload Reload config files 'reload'\n" + " stop Stop the daemon 'stop'\n" + " quit Close connection 'quit'\n" + " help Print this help page 'help'\n" + " Print command usage 'help COMMAND'"; } string MainLoop::getUpdates(time_t since, time_t until) diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index a8663caf..495a5970 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -92,35 +92,37 @@ private: /** * Decode and execute client message. * @param data the data string to decode (may be empty). + * @param connected set to false when the client connection shall be closed. * @param listening set to true when the client is in listening mode. + * @param running set to false when the server shall be stopped. * @return result string to send back to the client. */ - string decodeMessage(const string& data, bool& listening); + string decodeMessage(const string& data, bool& connected, bool& listening, bool& running); /** * Execute the read command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeRead(vector &args); /** * Execute the write command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeWrite(vector &args); /** * Execute the find command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeFind(vector &args); /** * Execute the listen command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param listening set to true when the client is in listening mode. * @return the result string. */ @@ -128,46 +130,62 @@ private: /** * Execute the state command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ - string executeState(); + string executeState(vector &args); /** * Execute the scan command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeScan(vector &args); /** * Execute the log command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeLog(vector &args); /** * Execute the raw command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeRaw(vector &args); /** * Execute the dump command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeDump(vector &args); /** * Execute the reload command. - * @param args the arguments passed to the command (starting with the command itself). + * @param args the arguments passed to the command (starting with the command itself), or empty for help. * @return the result string. */ string executeReload(vector &args); + /** + * Execute the stop command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param running set to false when the server shall be stopped. + * @return the result string. + */ + string executeStop(vector &args, bool& running); + + /** + * Execute the quit command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param connected set to false when the client connection shall be closed. + * @return the result string. + */ + string executeQuit(vector &args, bool& connected); + /** * Execute the help command. * @return the result string. diff --git a/src/ebusd/network.cpp b/src/ebusd/network.cpp index 95301e64..9bdedb98 100644 --- a/src/ebusd/network.cpp +++ b/src/ebusd/network.cpp @@ -113,13 +113,13 @@ void Connection::run() size_t datalen = 0; if (newData == true) { - if (m_socket->isValid() == true) - datalen = m_socket->recv(data, sizeof(data)-1); - else + if (m_socket->isValid() == false) break; + datalen = m_socket->recv(data, sizeof(data)-1); + // remove closed socket - if (datalen <= 0 || strcasecmp(data, "Q") == 0 || strcasecmp(data, "QUIT") == 0) + if (datalen <= 0) break; } @@ -137,6 +137,9 @@ void Connection::run() m_socket->send(result.c_str(), result.size()); m_listening = message.isListening(listenSince); + + if (message.isDisconnect()) + break; } } diff --git a/src/ebusd/network.h b/src/ebusd/network.h index 3ea3302d..996940e5 100644 --- a/src/ebusd/network.h +++ b/src/ebusd/network.h @@ -47,7 +47,7 @@ public: * @param listenSince start timestamp of listening update. */ NetMessage(const string data, const bool listening, const time_t listenSince) - : m_data(data), m_resultSet(false), m_listening(listening), m_listenSince(listenSince) + : m_data(data), m_resultSet(false), m_disconnect(false), m_listening(listening), m_listenSince(listenSince) { pthread_mutex_init(&m_mutex, NULL); pthread_cond_init(&m_cond, NULL); @@ -81,7 +81,8 @@ public: * Wait for the result being set and return the result string. * @return the result string. */ - string getResult() { + string getResult() + { pthread_mutex_lock(&m_mutex); while (m_resultSet == false) @@ -97,17 +98,19 @@ public: * @param result the result string. * @param listening whether the client is in listening mode. * @param listenUntil the end time to which to updates were added (exclusive). + * @param disconnect true when the client shall be disconnected. */ - void setResult(const string result, const bool listening, const time_t listenUntil) - { - m_result = result; - m_listening = listening; - m_listenSince = listenUntil; - m_resultSet = true; - pthread_mutex_lock(&m_mutex); - pthread_cond_signal(&m_cond); - pthread_mutex_unlock(&m_mutex); - } + void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) + { + m_result = result; + m_disconnect = disconnect; + m_listening = listening; + m_listenSince = listenUntil; + m_resultSet = true; + pthread_mutex_lock(&m_mutex); + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); + } /** * Return whether the client is in listening mode. @@ -116,6 +119,12 @@ public: */ bool isListening(time_t& listenSince) { listenSince = m_listenSince; return m_listening; } + /** + * Return whether the client shall be disconnected. + * @retur true when the client shall be disconnected. + */ + bool isDisconnect() { return m_disconnect; } + private: /** the data string */ string m_data; @@ -126,6 +135,9 @@ private: /** the result string */ string m_result; + /** set to true when the client shall be disconnected. */ + bool m_disconnect; + /** mutex variable for exclusive lock */ pthread_mutex_t m_mutex;