#include "Module.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REQUEST_METHOD(__METHODNAME__) public: static const char* Method () \ { \ return (__METHODNAME__); \ } class RestServer { private: class Administrator; public: class Request { private: Request (const Request&); Request& operator= (const Request&); public: enum Type { GET, PUT, POST, DELETE }; enum State { IDLE, LOADED, HANDLED, QUEUED }; enum Status { HTTP_OK = 200, HTTP_FAIL = 400 }; enum Content { TEXT }; public: Request(Content type, int recommendedBlockSize) : _state(IDLE), _recommendedBlockSize(recommendedBlockSize) { } virtual ~Request() { } public: void Response (boost::property_tree::ptree& data) { std::ostringstream ostr; if (_XML == false) { boost::property_tree::write_json (ostr, data); } else { boost::property_tree::write_xml (ostr, data); } _responseText = ostr.str(); } inline void SetHeader (const string& key, const string& value) { // MHD_add_response_header(_response, key.c_str(), value.c_str()); } inline bool IsXML () const { return (_XML); } inline bool IsJSON () const { return (!_XML); } protected: virtual void SetKey (const char* key, const char* value) { if ((::strcmp("type", key) == 0) && (::strcmp("xml", value) == 0)) { _XML = true; } } virtual Status Handle () = 0; private: friend class Administrator; bool Load( struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { ASSERT (_state != QUEUED); _XML = false; if (0 == ::strcmp (method, "GET")) { _type = GET; } else if (0 == ::strcmp (method, "PUT")) { _type = PUT; } else if (0 == ::strcmp (method, "POST")) { _type = POST; } else if (0 == ::strcmp (method, "DELETE")) { _type = DELETE; } if (MHD_get_connection_values ( connection, MHD_GET_ARGUMENT_KIND, Request::get_url_args, this) >= 0) { _state = LOADED; } return (_state == LOADED); } static int get_url_args(void *cls, MHD_ValueKind kind, const char *key, const char* value) { static_cast(cls)->SetKey(key, value); return MHD_YES; } inline void Execute () { _status = Handle (); _state = HANDLED; } void SendResponse (struct MHD_Connection *connection) { ASSERT (_state != QUEUED); if (_state != HANDLED) // So, IDLE or LOADED { // Send the default answer!!!! if (_XML == true) { _responseText = "ErrorBad data"; } else { _responseText = "ErrorBad data"; } _status = HTTP_FAIL; } _processed = 0; _state = QUEUED; struct MHD_Response* response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, _recommendedBlockSize, &ContentReaderCallback, this, &ContentReaderFreeCallback); MHD_add_response_header(response, "Content-Type", "text"); MHD_queue_response (connection, _status, response); MHD_destroy_response (response); } static long int ContentReaderCallback (void *cls, uint64_t pos, char *buf, size_t max) { ASSERT (static_cast(cls)->_state == QUEUED); const string& myBuffer = (static_cast(cls))->_responseText; size_t length = myBuffer.length() - pos; if (length > max) { length = max; } const char* source = &(myBuffer.data()[pos]); std::cout << "Transfer: " << length << " bytes." << std::endl; ::memcpy (buf, source, length); return (length == 0 ? MHD_CONTENT_READER_END_OF_STREAM : length); } static void ContentReaderFreeCallback (void* cls) { std::cout << "Completed !!" << std::endl; static_cast(cls)->_state = IDLE; } private: State _state; Status _status; Request::Type _type; bool _XML; uint32 _processed; string _responseText; int _recommendedBlockSize; }; private: class Specification { private: Specification (); Specification (const Specification&); Specification& operator= (const Specification&); public: Specification(const char* methodName) : _methodName(methodName) { } virtual ~Specification() { } public: inline const char* Method() const { return (_methodName); } private: friend class Administrator; virtual Request& TypedRequest() = 0; private: const char* _methodName; }; class Administrator { private: Administrator (const Administrator&); Administrator& operator= (const Administrator&); struct PlainCharCompare { bool operator () (const char *a,const char *b) const { return ::strcmp(a,b)<0; } }; typedef std::map SpecificationMap; public: Administrator() { static const char unknown_method[] = "Unknown Method."; _response = MHD_create_response_from_buffer ( sizeof(unknown_method)/sizeof(char), const_cast(unknown_method), MHD_RESPMEM_PERSISTENT); ASSERT(_response != NULL); } ~Administrator() { MHD_destroy_response (_response); ASSERT (_specifications.size() == 0); } public: inline void Register (Specification& spec) { SpecificationMap::iterator index = _specifications.find(spec.Method()); ASSERT (index == _specifications.end()); _specifications.insert(std::pair(spec.Method(), &spec)); } inline void Unregister (Specification& spec) { SpecificationMap::iterator index = _specifications.find(spec.Method()); ASSERT (index != _specifications.end()); _specifications.erase(index); } const char* Method (const uint32 index) const { if (index >= _specifications.size ()) { return (NULL); } SpecificationMap::const_iterator pos = _specifications.begin(); for (uint32 teller = 0; (teller != index) && (pos != _specifications.end()); teller++) { pos++; } if (pos != _specifications.end()) { return (pos->first); } return (NULL); } private: friend class RestServer; void Handle (struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { // Strawl through the list and find who will handle this.. std::map::iterator handler = _specifications.find(url); if (handler != _specifications.end()) { Request& typedRequest = handler->second->TypedRequest(); if (typedRequest.Load(connection, url, method, version, upload_data, upload_data_size, ptr) == true) { typedRequest.Execute (); } typedRequest.SendResponse (connection); } else { MHD_queue_response (connection, Request::HTTP_FAIL, _response); } } private: SpecificationMap _specifications; struct MHD_Response* _response; }; public: template class SpecificationType : public Specification { private: SpecificationType (const SpecificationType&); SpecificationType& operator= (const SpecificationType&); public: SpecificationType (Administrator& admin) : Specification(REQUESTTYPE::Method()), _admin(admin) { _admin.Register(*this); } virtual ~SpecificationType () { _admin.Unregister(*this); } private: virtual RestServer::Request& TypedRequest() { return (REQUESTTYPE::Instance()); } private: Administrator& _admin; }; private: RestServer(const RestServer&); RestServer& operator= (const RestServer&); public: RestServer(const uint16 portNumber) : _requestHandler(), _daemon (NULL), _portNumber (portNumber) { } ~RestServer () { Stop (); } bool Start () { if (_daemon == NULL) { _daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_POLL, _portNumber, 0, 0, &url_handler, this, MHD_OPTION_END); } return (_daemon != NULL); } bool Stop () { if (_daemon != NULL) { MHD_stop_daemon (_daemon); _daemon = NULL; } return (_daemon == NULL); } const char* Method (const uint32 index) const { return (_requestHandler.Method(index)); } protected: inline Administrator& GetAdministrator() { return (_requestHandler); } private: static int url_handler (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { // Let the administrator handle this... static_cast(cls)->_requestHandler.Handle(connection, url, method, version, upload_data, upload_data_size, ptr); return (MHD_YES); } private: Administrator _requestHandler; struct MHD_Daemon* _daemon; uint16 _portNumber; }; namespace System { void EraseAllChars(string& val, const char *chars_to_erase) { std::vector tokens; boost::split(tokens, val, boost::is_any_of(chars_to_erase), boost::token_compress_on ); val = ""; BOOST_FOREACH(string a, tokens) { val += a; } } void DiskInfo (boost::property_tree::ptree::iterator& ptit, const bool spaceInfo, const bool totalParts) { static const char command[] = "df | sed 's/ \\+/ /g' | tail -n +2 "; char line[255]; std::vector tokens; int i = 0,j; uint64_t totalspace = 0; uint64_t usedspace = 0; int32_t partnum = 0; FILE *fp = popen(command, "r"); if (fp != NULL) { string response; while (fgets(line, sizeof(line), fp) != 0) { response += string(line); } fclose(fp); boost::trim_if(response, boost::is_any_of(" \t\n")); boost::split( tokens, response, boost::is_any_of(" \t\n"), boost::token_compress_on ); j = tokens.size(); boost::property_tree::ptree diskinforoot ; boost::property_tree::ptree diskinfo; boost::property_tree::ptree::iterator pit ; while (i < j) { { boost::property_tree::ptree temp; pit = ptit->second.push_back(std::make_pair("FileSystem", temp)); } pit->second.push_back(std::make_pair("Name", tokens[i++])); try { if (spaceInfo == true) { totalspace += boost::lexical_cast(tokens[i]); } pit->second.push_back(std::make_pair("Size", tokens[i++])); usedspace += boost::lexical_cast(tokens[i]); pit->second.push_back(std::make_pair("Used", tokens[i++])); } catch ( boost::bad_lexical_cast& e) { } pit->second.push_back(std::make_pair("Avail", tokens[i++])); pit->second.push_back(std::make_pair("PercentUse", tokens[i++])); pit->second.push_back(std::make_pair("MountedOn", tokens[i++])); partnum++; } if (spaceInfo == true) { boost::property_tree::ptree temp; boost::format fmter("%1%"); pit = ptit->second.push_back(std::make_pair("SpaceInfo", temp)); fmter % totalspace; pit->second.push_back(std::make_pair("TotalSpace", fmter.str())); fmter.clear(); fmter % usedspace; pit->second.push_back(std::make_pair("UsedSpae", fmter.str())); fmter.clear(); } if (totalParts == true) { boost::property_tree::ptree temp; boost::format fmter("%1%"); fmter % partnum; ptit->second.push_back(std::make_pair("TotalParts", fmter.str())); fmter.clear(); } } } void ProcessInfo (boost::property_tree::ptree::iterator& ptit, const bool percentcpu, const bool percentmemory) { static const char command[] = "ps auxef | tail -n +2 |awk ' { printf \"%s %s %s %s \", $1, $2, $3, $3 ; for (i = 11; i <= NF; i++) {printf \"%s \", $i } print \"\" } ' "; char line[8096]; FILE *fp = popen(command, "r"); if (fp != NULL) { string read_line; boost::match_results< string::const_iterator > what; boost::property_tree::ptree prcinforoot ; boost::property_tree::ptree prcinfo; string::const_iterator start, end; boost::property_tree::ptree::iterator pit; boost::regex expression("(.*?) (.*?) (.*?) (.*?) (.*)"); boost::property_tree::ptree temp; while (fgets(line, sizeof(line), fp) != 0) { read_line = line; start = read_line.begin(); end = read_line.end(); if (!regex_search(start, end, what, expression, boost::match_default)) { continue; } if (what.size() != 6) { continue; } pit = ptit->second.push_back(make_pair("process", temp)); pit->second.push_back(std::make_pair("owner", string(what[1].first, what[1].second))); pit->second.push_back(std::make_pair("processid", string(what[2].first, what[2].second))); if (percentcpu) pit->second.push_back(std::make_pair("percentcpu", string(what[3].first, what[3].second))); if (percentmemory) pit->second.push_back(std::make_pair("percentmemory", string(what[4].first, what[4].second))); pit->second.push_back(std::make_pair("processcommand", string(what[5].first, what[5].second))); } fclose(fp); } } void CPUInfo (boost::property_tree::ptree::iterator& ptit) { static const char commandcpu[] = "cat /proc/cpuinfo | sed 's/\\s\\+: /:/g'"; char commandout[1048]; string line; FILE* fp = popen(commandcpu, "r"); if (fp != NULL) { boost::property_tree::ptree temp; string field; string value; size_t index; boost::property_tree::ptree::iterator pit; while (fgets(commandout, sizeof(commandout), fp) != 0) { line = commandout; EraseAllChars(line, ")( \r\n\t"); if (strncasecmp(line.c_str(),"processor:", 10) == 0) { pit = ptit->second.push_back(std::make_pair("cpus", temp)); } index = line.find(":"); if (string::npos == index) continue; field = line.substr(0, index); value = line.substr(index + 1); pit->second.push_back(make_pair(field, value)); } fclose(fp); } } void MemoryInfo (boost::property_tree::ptree::iterator& ptit) { static const char commandmemory[] = "cat /proc/meminfo | sed 's/:\\s\\+/:/g'"; FILE* fp = popen(commandmemory, "r"); if (fp != NULL) { boost::property_tree::ptree temp; string field; string value; size_t index; char commandout[1048]; string line; boost::property_tree::ptree::iterator pit = ptit->second.push_back(std::make_pair("memory", temp)); while (fgets(commandout, sizeof(commandout), fp) != 0) { line = commandout; EraseAllChars(line, ")( \n\r\t"); index = line.find(":"); if (string::npos == index) continue; field = line.substr(0, index ); value = line.substr(index + 1); pit->second.push_back(make_pair(field, value)); } fclose(fp); } } void OSInfo (boost::property_tree::ptree::iterator& ptit) { static const char commandos[] = "uname -a"; FILE* fp = popen(commandos, "r"); if (fp != NULL) { char commandout[1048]; if (fgets(commandout, sizeof(commandout), fp) != 0) { boost::property_tree::ptree temp; boost::property_tree::ptree::iterator pit = ptit->second.push_back(std::make_pair("os", temp)); pit->second.push_back(std::make_pair("osdetails", commandout)); } fclose(fp); } } }; class DiskInfoRequest: public RestServer::Request { REQUEST_METHOD("/diskinfo"); private: DiskInfoRequest (const DiskInfoRequest&); DiskInfoRequest& operator= (const DiskInfoRequest&); public: DiskInfoRequest() : RestServer::Request (RestServer::Request::TEXT, 4 * 1024) { } virtual ~DiskInfoRequest() { } public: static RestServer::Request& Instance () { static DiskInfoRequest _singleton; return (_singleton); } protected: virtual void SetKey (const char* key, const char* value) { if (::strcmp (key, "totalparts") == 0) { _totalParts = true; } else if (::strcmp (key, "spaceinfo") == 0) { _spaceInfo = true; } else RestServer::Request::SetKey(key, value); } virtual Request::Status Handle () { boost::property_tree::ptree inforoot ; boost::property_tree::ptree info; boost::property_tree::ptree::iterator info_tree = inforoot.push_back(std::make_pair("diskinfo", info)); System::DiskInfo (info_tree,_spaceInfo,_totalParts); Response (inforoot); return (Request::HTTP_OK); } private: bool _totalParts; bool _spaceInfo; }; class ProcessInfoRequest: public RestServer::Request { REQUEST_METHOD("/procinfo"); private: ProcessInfoRequest (const ProcessInfoRequest&); ProcessInfoRequest& operator= (const ProcessInfoRequest&); public: ProcessInfoRequest() : RestServer::Request (RestServer::Request::TEXT, 16 * 1024) { } virtual ~ProcessInfoRequest() { } public: static RestServer::Request& Instance () { static ProcessInfoRequest _singleton; return (_singleton); } protected: virtual void SetKey (const char* key, const char* value) { if (::strcmp (key, "percentmemory") == 0) { _memory = true; } else if (::strcmp (key, "percentcpu") == 0) { _cpu = true; } else RestServer::Request::SetKey(key, value); } virtual Request::Status Handle () { boost::property_tree::ptree inforoot ; boost::property_tree::ptree info; boost::property_tree::ptree::iterator info_tree = inforoot.push_back(std::make_pair("procinfo", info)); System::ProcessInfo (info_tree,_cpu,_memory); Response (inforoot); return (Request::HTTP_OK); } private: bool _memory; bool _cpu; }; class SysInfoRequest: public RestServer::Request { REQUEST_METHOD("/sysinfo"); private: SysInfoRequest (const SysInfoRequest&); SysInfoRequest& operator= (const SysInfoRequest&); public: SysInfoRequest() : RestServer::Request (RestServer::Request::TEXT, 4 * 1024) { } virtual ~SysInfoRequest() { } public: static RestServer::Request& Instance () { static SysInfoRequest _singleton; return (_singleton); } protected: virtual void SetKey (const char* key, const char* value) { if (::strcmp (key, "cpu") == 0) { _cpu = true; } else if (::strcmp (key, "memory") == 0) { _memory = true; } else if (::strcmp (key, "os") == 0) { _os = true; } else RestServer::Request::SetKey(key, value); } virtual Request::Status Handle () { boost::property_tree::ptree inforoot ; boost::property_tree::ptree info; boost::property_tree::ptree::iterator info_tree = inforoot.push_back(std::make_pair("sysinfo", info )); if (_cpu == true) System::CPUInfo (info_tree); if (_memory == true) System::MemoryInfo (info_tree); if (_os == true) System::OSInfo (info_tree); Response (inforoot); return (Request::HTTP_OK); } private: bool _cpu; bool _memory; bool _os; }; class MyRestServer : public RestServer { private: MyRestServer (const MyRestServer&); MyRestServer& operator= (const MyRestServer&); public: MyRestServer (uint16 portNumber) : RestServer (portNumber), _SysInfoHandler(RestServer::GetAdministrator()), _DiskInfoHandler(RestServer::GetAdministrator()), _ProcessInfoHandler(RestServer::GetAdministrator()) { } ~MyRestServer () { } private: SpecificationType _SysInfoHandler; SpecificationType _DiskInfoHandler; SpecificationType _ProcessInfoHandler; }; static int shouldNotExit = 1; void handle_term(int signo) { shouldNotExit = 0; } int main (int argc, char *const *argv) { if (argc != 2){ printf ("%s PORT\n", argv[0]); exit(1); } // daemon(0,0); signal(SIGTERM, handle_term); int port = atoi(argv[1]); MyRestServer restServer (port); std::cout << "Subscribed methods:" << std::endl; const char* method; int index = 0; while ( (method = restServer.Method(index)) != NULL) { index++; std::cout << index << ": " << method << std::endl; } std::cout << "Starting server..." << std::endl; restServer.Start (); while(shouldNotExit) { sleep(1); } std::cout << "Stoping server..." << std::endl; restServer.Stop(); return 0; }