From c3457fb55c1b3be997a2677b5519db95c8865bbb Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Sat, 12 Jul 2014 17:05:09 +0200 Subject: [PATCH] Add signal USR2 management at core application and diameter launcher to manage operations --- example/diameter/launcher/main.cpp | 156 ++++++++++++++++++++--------- include/anna/app/Application.hpp | 11 +- source/app/Application.cpp | 24 +++-- 3 files changed, 137 insertions(+), 54 deletions(-) diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index 3c43302..dce3235 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -90,6 +90,9 @@ class Response; } } +#define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.tasks.input" +#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.tasks.output" + // Auxiliary message for sendings anna::diameter::comm::Message G_commMsgSent2c, G_commMsgSent2e, G_commMsgFwd2c, G_commMsgFwd2e; @@ -166,7 +169,6 @@ private: void evRequest(anna::comm::ClientSocket&, const anna::http::Request& request) throw(anna::RuntimeException); void evResponse(anna::comm::ClientSocket&, const anna::http::Response&) throw(anna::RuntimeException) {;} - void sendOperation(const std::string &, std::string &) throw(anna::RuntimeException); }; class MyCommunicator : public anna::comm::Communicator { @@ -233,6 +235,7 @@ public: void baseProtocolSetupAsClient(void) throw(anna::RuntimeException); anna::diameter::comm::Entity *getEntity() throw() { return a_entity; } anna::diameter::comm::LocalServer* getDiameterLocalServer() throw() { return a_diameterLocalServer; } + void eventOperation(const std::string &, std::string &) throw(anna::RuntimeException); bool logEnabled() const throw() { return (((a_logFile == "") || (a_logFile == "null")) ? false : true); } void writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw(); void writeLogFile(const anna::diameter::codec::Message & decodedMessage, const std::string &logExtension, const std::string &detail) const throw(); @@ -243,6 +246,7 @@ public: anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); void resetStatistics() throw() { a_myDiameterEngine->resetStatistics(); } void resetCounters() throw(); + void signalUSR2() throw(anna::RuntimeException); std::string help() const throw(); std::string programmedAnswers2e() const throw(); std::string programmedAnswers2c() const throw(); @@ -494,6 +498,47 @@ void Launcher::resetCounters() throw() { oamDiameterComm.resetCounters(); } +void Launcher::signalUSR2() throw(anna::RuntimeException) { + + LOGNOTICE( + std::string msg = "Captured signal SIGUSR2. Reading tasks at '"; + msg += SIGUSR2_TASKS_INPUT_FILENAME; + msg += "' (results will be written at '"; + msg += SIGUSR2_TASKS_OUTPUT_FILENAME; + msg += "')"; + anna::Logger::notice(msg, ANNA_FILE_LOCATION); + ); + + // Operation: + std::string line; + std::string response_content; + + std::ifstream in_file (SIGUSR2_TASKS_INPUT_FILENAME); + std::ofstream out_file (SIGUSR2_TASKS_OUTPUT_FILENAME); + + if (!in_file.is_open()) throw RuntimeException("Unable to read tasks", ANNA_FILE_LOCATION); + if (!out_file.is_open()) throw RuntimeException("Unable to write tasks", ANNA_FILE_LOCATION); + + while (getline (in_file, line)) + { + LOGDEBUG( + std::string msg = "Processing line: "; + msg += line; + anna::Logger::debug(msg, ANNA_FILE_LOCATION); + ); + + try { + eventOperation(line, response_content); + } catch (RuntimeException &ex) { + ex.trace(); + } + out_file << response_content; + } + in_file.close(); + out_file.close(); +} + + std::string Launcher::help() const throw() { std::string result = "\n"; result += "\n ------------- HELP -------------\n"; @@ -504,14 +549,21 @@ std::string Launcher::help() const throw() { result += "\nencoder/decoder/loader function could be deployed to reinterpret certain external flow and"; result += "\nsend it to another process."; result += "\n"; - result += "\nAs any other ANNA process, context dump could be retrieved sending -10 signal:"; - result += "\n kill -10 ; vi /var/tmp/anna.context."; + result += "\nAs any other ANNA process, context dump could be retrieved sending SIGUSR1 signal:"; + result += "\n kill -10 "; + result += "\n or"; + result += "\n kill -s SIGUSR1 "; + result += "\n and then"; + result += "\n vi /var/tmp/anna.context."; result += "\n"; result += "\nA complete xml report will show all the context information (counters, alarms, statistics,"; result += "\nhandlers, diameter dictionary, etc.), and a powerful log module could dump all the events"; result += "\nprocessed and flow information. Statistics could be analized at context dump and optionally"; result += "\nwritten to disk as sample files with all the events measurements."; result += "\n"; + result += "\nAlso SIGUSR2 is handled as an alternative to http operation interface."; + result += "\nWe will talk later about this management interface."; + result += "\n"; result += "\nProcess traces are dump on \"launcher.traces\" and could have any trace level (POSIX levels):"; result += "\nusually 'debug' or 'warning'. See ANNA documentation."; result += "\n"; @@ -630,6 +682,20 @@ std::string Launcher::help() const throw() { result += "\n burst|look| Show programmed burst message for order provided."; result += "\n"; result += "\n"; + result += "\nAnother way to execute operations is to create a task file and send SIGUSR2 signal to interpret it:"; + result += "\n echo \"<\" > "; result += SIGUSR2_TASKS_INPUT_FILENAME; + result += "\n then"; + result += "\n kill -12 "; + result += "\n or"; + result += "\n kill -s SIGUSR2 "; + result += "\n and then see the results:"; + result += "\n cat "; result += SIGUSR2_TASKS_OUTPUT_FILENAME; + result += "\n"; + result += "\nYou could place more than one line (task) in the input file. Output reports will be appended in that"; + result += "\n case over the output file."; + result += "\n"; + result += "\n"; + return result; } @@ -1385,7 +1451,8 @@ throw(anna::RuntimeException) { std::string response_content; try { - sendOperation(body_content, response_content); + Launcher& my_app = static_cast (anna::app::functions::getApp()); + my_app.eventOperation(body_content, response_content); } catch (RuntimeException &ex) { ex.trace(); } @@ -1410,9 +1477,8 @@ throw(anna::RuntimeException) { } } -void MyHandler::sendOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) { - LOGMETHOD(anna::TraceMethod tm("MyHandler", "sendOperation", ANNA_FILE_LOCATION)); - Launcher& my_app = static_cast (anna::app::functions::getApp()); +void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) { + LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION)); CommandLine& cl(anna::CommandLine::instantiate()); LOGDEBUG(anna::Logger::debug(operation, ANNA_FILE_LOCATION)); response_content = "Operation processed with exception. See traces\n"; // supposed @@ -1420,15 +1486,15 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons // Help: if (operation == "help") { - std::cout << my_app.help() << std::endl; + std::cout << help() << std::endl; response_content = "Help dumped on stdout and information-level traces (launcher.traces file)\n"; return; } // Reset performance data: if (operation == "collect") { - my_app.resetCounters(); - my_app.resetStatistics(); + resetCounters(); + resetStatistics(); response_content = "All process counters & statistic information have been reset\n"; return; } @@ -1440,7 +1506,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons //LOGDEBUG(anna::Logger::debug(anna::functions::asString("Number of operation parameters: %d", numParams), ANNA_FILE_LOCATION)); if (numParams > 2) { - LOGWARNING(anna::Logger::warning(my_app.help(), ANNA_FILE_LOCATION)); + LOGWARNING(anna::Logger::warning(help(), ANNA_FILE_LOCATION)); throw anna::RuntimeException("Wrong body content format on HTTP Request", ANNA_FILE_LOCATION); } @@ -1483,7 +1549,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons outfile.close(); infile.close(); } else if ((opType == "hide") || (opType == "show") || (opType == "hidden") || (opType == "shown")) { - anna::diameter::comm::Entity *entity = my_app.getEntity(); + anna::diameter::comm::Entity *entity = getEntity(); if (!entity) throw anna::RuntimeException("No entity configured to send messages", ANNA_FILE_LOCATION); @@ -1493,25 +1559,25 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons key += "|"; key += param2; - if (opType == "hide") my_app.getMyDiameterEngine()->findClientSession(key)->hide(); + if (opType == "hide") getMyDiameterEngine()->findClientSession(key)->hide(); - if (opType == "show") my_app.getMyDiameterEngine()->findClientSession(key)->show(); + if (opType == "show") getMyDiameterEngine()->findClientSession(key)->show(); - if (opType == "hidden") result = my_app.getMyDiameterEngine()->findClientSession(key)->hidden() ? "true" : "false"; + if (opType == "hidden") result = getMyDiameterEngine()->findClientSession(key)->hidden() ? "true" : "false"; - if (opType == "shown") result = my_app.getMyDiameterEngine()->findClientSession(key)->shown() ? "true" : "false"; + if (opType == "shown") result = getMyDiameterEngine()->findClientSession(key)->shown() ? "true" : "false"; } else { std::string address; int port; anna::functions::getAddressAndPortFromSocketLiteral(param1, address, port); - if (opType == "hide") my_app.getMyDiameterEngine()->findServer(address, port)->hide(); + if (opType == "hide") getMyDiameterEngine()->findServer(address, port)->hide(); - if (opType == "show") my_app.getMyDiameterEngine()->findServer(address, port)->show(); + if (opType == "show") getMyDiameterEngine()->findServer(address, port)->show(); - if (opType == "hidden") result = my_app.getMyDiameterEngine()->findServer(address, port)->hidden() ? "true" : "false"; + if (opType == "hidden") result = getMyDiameterEngine()->findServer(address, port)->hidden() ? "true" : "false"; - if (opType == "shown") result = my_app.getMyDiameterEngine()->findServer(address, port)->shown() ? "true" : "false"; + if (opType == "shown") result = getMyDiameterEngine()->findServer(address, port)->shown() ? "true" : "false"; } } else { if (opType == "hide") entity->hide(); @@ -1526,7 +1592,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (numParams != 1) throw anna::RuntimeException("Wrong body content format on HTTP Request for 'sendxml/sendxml2e' operation (missing parameter)", ANNA_FILE_LOCATION); - anna::diameter::comm::Entity *entity = my_app.getEntity(); + anna::diameter::comm::Entity *entity = getEntity(); if (!entity) throw anna::RuntimeException("No entity configured to send the message", ANNA_FILE_LOCATION); @@ -1538,17 +1604,17 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons bool success = entity->send(G_commMsgSent2e, cl.exists("balance")); // Detailed log: - if (my_app.logEnabled()) { + if (logEnabled()) { anna::diameter::comm::Server *usedServer = entity->getLastUsedResource(); anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL; std::string detail = usedClientSession ? usedClientSession->asString() : ""; // esto no deberia ocurrir - my_app.writeLogFile(G_codecMsg, (success ? "sent2e" : "send2eError"), detail); + writeLogFile(G_codecMsg, (success ? "sent2e" : "send2eError"), detail); } } else if ((opType == "burst")) { if (numParams < 1) throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (missing action parameter)", ANNA_FILE_LOCATION); - anna::diameter::comm::Entity *entity = my_app.getEntity(); + anna::diameter::comm::Entity *entity = getEntity(); if (!entity) throw anna::RuntimeException("No entity configured to use burst feature", ANNA_FILE_LOCATION); @@ -1567,7 +1633,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (param1 == "clear") { result = "Removed "; - result += anna::functions::asString(my_app.clearBurst()); + result += anna::functions::asString(clearBurst()); result += " elements."; } else if (param1 == "load") { if (param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION); @@ -1577,7 +1643,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (G_codecMsg.isAnswer()) throw anna::RuntimeException("Cannot load diameter answers for burst feature", ANNA_FILE_LOCATION); try { G_codecMsg.valid(); } catch (anna::RuntimeException &ex) { ex.trace(); } // at least we need to see validation errors although it will continue loading (see validation mode configured in launcher) - int position = my_app.loadBurstMessage(G_codecMsg.code()); + int position = loadBurstMessage(G_codecMsg.code()); result = "Loaded '"; result += param2; result += "' file into burst list position "; @@ -1586,7 +1652,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION); int initialLoad = atoi(param2.c_str()); - int processed = my_app.startBurst(initialLoad); + int processed = startBurst(initialLoad); if (processed > 0) { result = "Initial load completed for "; @@ -1596,7 +1662,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons } else if (param1 == "push") { if (param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION); - int pushed = my_app.pushBurst(atoi(param2.c_str())); + int pushed = pushBurst(atoi(param2.c_str())); if (pushed > 0) { result = "Pushed "; @@ -1607,7 +1673,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION); int releaseLoad = atoi(param2.c_str()); - int popped = my_app.popBurst(releaseLoad); + int popped = popBurst(releaseLoad); if (popped > 0) { result = "Burst popped for "; @@ -1615,7 +1681,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons result += "."; } } else if (param1 == "stop") { - int left = my_app.stopBurst(); + int left = stopBurst(); if (left != -1) { result += anna::functions::entriesAsString(left, "message"); @@ -1625,12 +1691,12 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (param2 == "") param2 = "yes"; bool repeat = (param2 == "yes"); - my_app.repeatBurst(repeat); + repeatBurst(repeat); result += (repeat ? "Mode on." : "Mode off."); } else if (param1 == "send") { if (param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION); - int sent = my_app.sendBurst(atoi(param2.c_str())); + int sent = sendBurst(atoi(param2.c_str())); if (sent > 0) { result = "Sent "; @@ -1640,13 +1706,13 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons } else if (param1 == "goto") { if (param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION); - result = my_app.gotoBurst(atoi(param2.c_str())); + result = gotoBurst(atoi(param2.c_str())); result += "."; } else if (param1 == "look") { if (param2 == "") throw anna::RuntimeException("Missing order position for burst look operation", ANNA_FILE_LOCATION); result = "\n\n"; - result += my_app.lookBurst(atoi(param2.c_str())); + result += lookBurst(atoi(param2.c_str())); result += "\n\n"; } else { throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). See help", ANNA_FILE_LOCATION); @@ -1655,7 +1721,7 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons if (numParams != 1) throw anna::RuntimeException("Wrong body content format on HTTP Request for 'sendxml2c' operation (missing parameter)", ANNA_FILE_LOCATION); - anna::diameter::comm::LocalServer *localServer = my_app.getDiameterLocalServer(); + anna::diameter::comm::LocalServer *localServer = getDiameterLocalServer(); if (!localServer) throw anna::RuntimeException("No local server configured to send the message", ANNA_FILE_LOCATION); @@ -1667,10 +1733,10 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons bool success = localServer->send(G_commMsgSent2c); // Detailed log: - if (my_app.logEnabled()) { + if (logEnabled()) { anna::diameter::comm::ServerSession *usedServerSession = localServer->getLastUsedResource(); std::string detail = usedServerSession ? usedServerSession->asString() : ""; // esto no deberia ocurrir - my_app.writeLogFile(G_codecMsg, (success ? "sent2c" : "send2cError"), detail); + writeLogFile(G_codecMsg, (success ? "sent2c" : "send2cError"), detail); } } else if (opType == "loadxml") { if (numParams != 1) @@ -1685,12 +1751,12 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons int diameterServerSessions = atoi(param1.c_str()); - if (!my_app.getDiameterLocalServer()) - my_app.startDiameterServer(diameterServerSessions); + if (!getDiameterLocalServer()) + startDiameterServer(diameterServerSessions); else - my_app.getDiameterLocalServer()->setMaxConnections(diameterServerSessions); + getDiameterLocalServer()->setMaxConnections(diameterServerSessions); } else if ((opType == "answerxml") || (opType == "answerxml2c")) { - anna::diameter::comm::LocalServer *localServer = my_app.getDiameterLocalServer(); + anna::diameter::comm::LocalServer *localServer = getDiameterLocalServer(); if (!localServer) throw anna::RuntimeException("Operation not applicable (no own diameter server has been configured)", ANNA_FILE_LOCATION); @@ -1716,12 +1782,12 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons G_reactingAnswers2C[code] = message; } else { // answers query on stdout - std::cout << my_app.programmedAnswers2c() << std::endl; + std::cout << programmedAnswers2c() << std::endl; response_content = "Programmed answers dumped on stdout\n"; return; } } else if ((opType == "answerxml2e")) { - anna::diameter::comm::Entity *entity = my_app.getEntity(); + anna::diameter::comm::Entity *entity = getEntity(); if (!entity) throw anna::RuntimeException("Operation not applicable (no diameter entity has been configured)", ANNA_FILE_LOCATION); @@ -1747,12 +1813,12 @@ void MyHandler::sendOperation(const std::string &operation, std::string &respons G_reactingAnswers2E[code] = message; } else { // answers query on stdout - std::cout << my_app.programmedAnswers2e() << std::endl; + std::cout << programmedAnswers2e() << std::endl; response_content = "Programmed answers dumped on stdout\n"; return; } } else { - LOGWARNING(anna::Logger::warning(my_app.help(), ANNA_FILE_LOCATION)); + LOGWARNING(anna::Logger::warning(help(), ANNA_FILE_LOCATION)); throw anna::RuntimeException("Wrong body content format on HTTP Request. Unsupported/unrecognized operation type", ANNA_FILE_LOCATION); } diff --git a/include/anna/app/Application.hpp b/include/anna/app/Application.hpp index c34c46a..153f5b9 100644 --- a/include/anna/app/Application.hpp +++ b/include/anna/app/Application.hpp @@ -231,11 +231,15 @@ protected: virtual void run() throw(RuntimeException) = 0; /** - Metodo manejador que podemos re-escribir para tratar la recepcion de la senhal USR1. - Por defecto + Handler for SIGUSR1. Application context written by default. */ virtual void signalUSR1() throw(RuntimeException); + /** + Handler for SIGUSR2. Nothing done by default. + */ + virtual void signalUSR2() throw(RuntimeException); + /** Metodo manejador que podemos re-escribir para tratar la recepcion de la senhal SIGTERM. */ @@ -316,7 +320,8 @@ private: void stopComponents() throw(RuntimeException); void sendSignalToChilds(const int signal) throw(); - static void handlerSignalUSR1(int) throw(); + void signalUSR(int) throw(RuntimeException); + static void handlerSignalUSR(int) throw(); static void handlerSignalTerminate(int) throw(); static void handlerChildTerminate(int sig) throw(); diff --git a/source/app/Application.cpp b/source/app/Application.cpp index acde252..e838e88 100644 --- a/source/app/Application.cpp +++ b/source/app/Application.cpp @@ -73,7 +73,8 @@ app::Application::Application(const char* shortName, const char* title, const ch a_version(version), a_title(title), a_enableGPL(false) { - sigset(SIGUSR1, handlerSignalUSR1); + sigset(SIGUSR1, handlerSignalUSR); + sigset(SIGUSR2, handlerSignalUSR); sigset(SIGTERM, handlerSignalTerminate); sigignore(SIGINT); app::sccs::activate(); @@ -390,30 +391,41 @@ throw() { return app; } +void app::Application::signalUSR(int signal) +throw(RuntimeException) { + if (signal == SIGUSR1) signalUSR1(); + else if (signal == SIGUSR2) signalUSR2(); +} + void app::Application::signalUSR1() throw(RuntimeException) { writeContext(anna::functions::asString("/var/tmp/anna.context.%05d", getPid())); } +void app::Application::signalUSR2() +throw(RuntimeException) { + Logger::notice("Captured signal SIGUSR2. Nothing implemented at the moment", ANNA_FILE_LOCATION); +} + void app::Application::sendSignalToChilds(const int signal) throw() { for(pid_iterator ii = pid_begin(), maxii = pid_end(); ii != maxii; ii ++) kill(pid(ii), signal); } -void app::Application::handlerSignalUSR1(int) +void app::Application::handlerSignalUSR(int signal) throw() { - sigignore(SIGUSR1); + sigignore(signal); try { Application& app = anna::app::functions::getApp(); - app.sendSignalToChilds(SIGUSR1); - app.signalUSR1(); + app.sendSignalToChilds(signal); + app.signalUSR(signal); } catch(Exception& ex) { ex.trace(); } - sigset(SIGUSR1, handlerSignalUSR1); + sigset(signal, handlerSignalUSR); } void app::Application::handlerSignalTerminate(int) -- 2.20.1