#ifndef __JRUNCONTROL__JCLIENT__ #define __JRUNCONTROL__JCLIENT__ #include #include #include #include "JLang/JException.hh" #include "JLang/JString.hh" #include "JLang/JWhiteSpacesFacet.hh" #include "JDAQ/JDAQTags.hh" #include "JRuncontrol/JRuncontrolToolkit.hh" #include "JNet/JPrefix.hh" #include "JSystem/JShell.hh" /** * \author mdejong */ namespace KM3NETDAQ { /** * Client data structure. */ class JClient { public: static std::string SERVER; //!< host name of message server static std::string LOGGER; //!< host name of message logger /** * Client modi. */ enum JMode { UNKNOWN = -1, SLEEP = 0, ACTIVE, IGNORE = -2, ILLEGAL = -3 }; /** * Default constructor. */ JClient() : name (), hostname (), start_command(), mode(UNKNOWN), born(0), died(0), is_alive(false), state_name("?"), event_name("?"), event_number(-1) {} /** * Constructor. * * \param __name name of client * \param __hostname host name * \param __start_command start command */ JClient(const std::string& __name, const std::string& __hostname, const std::string& __start_command = "") : name (__name), hostname (__hostname), start_command(__start_command), mode(UNKNOWN), born(0), died(0), is_alive(false), state_name("?"), event_name("?"), event_number(-1) { configure(); } /** * Start process. * The start command is issued on the (remote) host. */ void start() { using namespace std; using namespace JPP; if (start_command != "") { JShell& shell = JShell::getInstance(); shell << start_command << endl; shell.flush(cout); } } /** * Stop process. * A kill command is issued on the (remote) host. * To determine the PID for the kill command, the start command is compared to * the output of a ps command on the (remote) host. * * \param signal signal */ void stop(const int signal = -9) { using namespace std; using namespace JPP; if (start_command != "") { const string header = "__header___"; const string trailer = "__trailer__"; ostringstream os; os << "ssh " << hostname << ' ' << "\"" << "echo" << ' ' << header << ";" << "ps hax -o '%p %a'" << ";" << "echo" << ' ' << trailer << "\""; JShell& shell = JShell::getInstance(); shell << os.str() << endl; os.str(""); string buffer; while (getline(shell, buffer) && buffer.find(header) == string::npos) {} istringstream is; while (getline(shell, buffer) && buffer.find(trailer) == string::npos) { is.clear(); is.str(buffer); int pid; if (is >> pid && getline(is >> ws, buffer)) { if (start_command.find(buffer) != string::npos) { os << "ssh " << hostname << ' ' << "\"" << "kill " << signal << " " << pid << " &/dev/null" << "\""; } } } shell.flush(); if (os.str() != "") { shell << os.str() << endl; shell.flush(); } } } /** * Get name of run control client. * * \return name */ const std::string& getName() const { return name; } /** * Get host name of run control client. * * \return host name */ const std::string& getHostname() const { return hostname; } /** * Get full name of run control client. * * \return full name */ const std::string& getFullName() const { return full_name; } /** * Get start command of run control client. * * \return name */ const std::string& getStartCommand() const { return start_command; } /** * Check sleep mode. * * \return true if sleep; else false */ bool isSleep() const { return mode == SLEEP; } /** * Check active mode. * * \return true if active; else false */ bool isActive() const { return mode == ACTIVE; } /** * Check ignore mode. * * \return true if ignore; else false */ bool isIgnore() const { return mode == IGNORE; } /** * Check illegal mode. * * \return true if illegal; else false */ bool isIllegal() const { return mode == ILLEGAL; } /** * Update client. * The client parameters are updated according to the ControlHost tag and message. * * \param tag tag * \param buffer message * \return true if OK; else false */ bool update(const JNET::JTag& tag, const std::string& buffer) { using namespace std; using JLANG::JWhiteSpacesFacet; if (tag == JNET::DISPTAG_Born) { this->is_alive = true; this->born += 1; return true; } else if (tag == JNET::DISPTAG_Died) { this->is_alive = false; this->died += 1; return true; } else if (tag == RC_REPLY) { string key; string ip; string name; JEvent_t event; string state; istringstream is(buffer); const locale loc(is.getloc(), new JWhiteSpacesFacet(is.getloc(), TOKEN_DELIMETER)); is.imbue(loc); if (is >> key >> ip >> name >> event >> state && key == RUN_CONTROL_CLIENT) { this->event_name = event.getName(); this->event_number = event.getNumber(); this->state_name = state; return true; } } return false; } /** * Get name of current state. * * \return state name */ const std::string& getStatename() const { return state_name; } /** * Get name of last event. * * \return event name */ const std::string& getEventname() const { return event_name; } /** * Get number of last event. * * \return event number */ int getEventnumber() const { return event_number; } /** * Get alive status. * * \return true if alive; else false */ bool getAlive() const { return is_alive; } /** * Set alive status. * * \param alive alive */ void setAlive(const bool alive) { is_alive = alive; } /** * Get mode. * * \return mode */ JMode getMode() const { return mode; } /** * Set mode. * * \param mode mode */ void setMode(const JMode mode) { this->mode = mode; } /** * Get born count. * * \return number of times born */ int getBorn() const { return born; } /** * Get died count. * * \return number of times died */ int getDied() const { return died; } /** * Read client from input. * * \param in istream * \param client client * \return istream */ friend inline std::istream& operator>>(std::istream& in, JClient& client) { in >> client.name; in >> client.hostname; if (in) { getline(in >> std::ws, client.start_command); in.clear(); // clear error state of input stream if no start command is available. client.configure(); } return in; } /** * Write client to output. * * \param out ostream * \param client client * \return ostream */ friend inline std::ostream& operator<<(std::ostream& out, const JClient& client) { out << client.name; out << ' '; out << client.hostname; out << ' '; out << client.start_command; return out; } protected: /** * Configure client parameters. * In this, the following tokens in the start command are substituted: * $HOST$ by \; * $NAME$ by \; * $SERVER$ by JClient::SERVER; * $LOGGER$ by JClient::LOGGER; * $ARGS$ by part following last '/' in \; */ void configure() { using std::string; full_name = KM3NETDAQ::getFullName(hostname, name); start_command = start_command.trim(); start_command = start_command.replace(" ", " "); start_command.replace("$HOST$", hostname); start_command.replace("$NAME$", name); start_command.replace("$SERVER$", SERVER); start_command.replace("$LOGGER$", LOGGER); string::size_type pos = name.find_last_of(CLIENTNAME_DELIMETER); if (pos != string::npos) { start_command.replace("$ARGS$", name.substr(pos + 1)); } } std::string name; std::string hostname; JLANG::JString start_command; std::string full_name; JMode mode; int born; int died; bool is_alive; std::string state_name; std::string event_name; int event_number; }; } #endif