diff --git a/node/Node.cpp b/node/Node.cpp index 08b08fc1e..b5c23ed9c 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -68,6 +68,7 @@ #include "Mutex.hpp" #include "Multicaster.hpp" #include "CMWC4096.hpp" +#include "RPC.hpp" #include "../version.h" @@ -210,6 +211,7 @@ Node::~Node() { _NodeImpl *impl = (_NodeImpl *)_impl; + delete impl->renv.rpc; delete impl->renv.sysEnv; delete impl->renv.topology; delete impl->renv.sw; @@ -315,6 +317,7 @@ Node::ReasonForTermination Node::run() _r->sw = new Switch(_r); _r->topology = new Topology(_r,(_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db").c_str()); _r->sysEnv = new SysEnv(_r); + _r->rpc = new RPC(_r); // TODO: make configurable bool boundPort = false; @@ -338,6 +341,25 @@ Node::ReasonForTermination Node::run() return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization"); } + try { + std::map pluginsd(Utils::listDirectory((_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d").c_str())); + for(std::map::iterator ppath(pluginsd.begin());ppath!=pluginsd.end();++ppath) { + if (!ppath->second) { + try { + std::string funcName(ppath->first.substr(0,ppath->first.rfind('.'))); + LOG("loading plugins.d/%s as RPC function %s",ppath->first.c_str(),funcName.c_str()); + _r->rpc->loadLocal(funcName.c_str(),(_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d" + ZT_PATH_SEPARATOR_S + ppath->first).c_str()); + } catch (std::exception &exc) { + LOG("failed to load plugin plugins.d/%s: %s",ppath->first.c_str(),exc.what()); + } catch ( ... ) { + LOG("failed to load plugin plugins.d/%s: (unknown exception)",ppath->first.c_str()); + } + } + } + } catch ( ... ) { + TRACE("unknown exception attempting to enumerate and load plugins"); + } + try { uint64_t lastPingCheck = 0; uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately diff --git a/node/RPC.cpp b/node/RPC.cpp index e6591d7d4..5a9cd7195 100644 --- a/node/RPC.cpp +++ b/node/RPC.cpp @@ -37,6 +37,8 @@ namespace ZeroTier { +#ifndef __WINDOWS__ + RPC::LocalService::LocalService(const char *dllPath) throw(std::invalid_argument) : _handle((void *)0), @@ -111,6 +113,8 @@ std::pair< int,std::vector > RPC::LocalService::operator()(const st return std::pair< int,std::vector >(rcount,results); } +#endif // __WINDOWS__ + RPC::RPC(const RuntimeEnvironment *renv) : _r(renv) { @@ -123,17 +127,35 @@ RPC::~RPC() co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_CANCELLED,std::vector()); } +#ifndef __WINDOWS__ for(std::map::iterator s(_rpcServices.begin());s!=_rpcServices.end();++s) delete s->second; +#endif } std::pair< int,std::vector > RPC::callLocal(const std::string &name,const std::vector &args) { +#ifdef __WINDOWS__ + return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); +#else Mutex::Lock _l(_rpcServices_m); std::map::iterator s(_rpcServices.find(name)); if (s == _rpcServices.end()) return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); return ((*(s->second))(args)); +#endif +} + +void RPC::loadLocal(const char *name,const char *path) + throw(std::invalid_argument) +{ +#ifdef __WINDOWS__ + throw std::invalid_argument("RPC plugins not supported on Windows (yet?)"); +#else + LocalService *s = new LocalService(path); + Mutex::Lock _l(_rpcServices_m); + _rpcServices[std::string(name)] = s; +#endif } uint64_t RPC::callRemote( diff --git a/node/RPC.hpp b/node/RPC.hpp index e31b5f6de..669bf5aef 100644 --- a/node/RPC.hpp +++ b/node/RPC.hpp @@ -139,6 +139,16 @@ public: */ std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args); + /** + * Load a plugin + * + * @param name Name of RPC function + * @param path Path to plugin DLL + * @throws std::invalid_argument Unable to properly load or resolve symbol(s) in DLL + */ + void loadLocal(const char *name,const char *path) + throw(std::invalid_argument); + /** * Call a remote service * @@ -165,8 +175,10 @@ public: private: const RuntimeEnvironment *_r; +#ifndef __WINDOWS__ std::map _rpcServices; Mutex _rpcServices_m; +#endif struct RemoteCallOutstanding { diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index bc63543ad..35c2de3fd 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -42,6 +42,7 @@ class Topology; class SysEnv; class Multicaster; class CMWC4096; +class RPC; /** * Holds global state for an instance of ZeroTier::Node @@ -65,7 +66,9 @@ public: demarc((Demarc *)0), multicaster((Multicaster *)0), sw((Switch *)0), - topology((Topology *)0) + topology((Topology *)0), + sysEnv((SysEnv *)0), + rpc((RPC *)0) { } @@ -76,6 +79,9 @@ public: Identity identity; + // Order matters a bit here. These are constructed in this order + // and then deleted in the opposite order on Node exit. + Logger *log; // may be null CMWC4096 *prng; NodeConfig *nc; @@ -84,6 +90,7 @@ public: Switch *sw; Topology *topology; SysEnv *sysEnv; + RPC *rpc; }; } // namespace ZeroTier diff --git a/node/Utils.cpp b/node/Utils.cpp index 7b2a17af3..7a4d51ba0 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -29,14 +29,13 @@ #include #include #include -#include "Utils.hpp" -#include "Mutex.hpp" #if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) #include #include #include #include +#include #endif #ifdef _WIN32 @@ -46,6 +45,9 @@ #include #include +#include "Utils.hpp" +#include "Mutex.hpp" + namespace ZeroTier { const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; @@ -214,6 +216,29 @@ const char Utils::base64DecMap[128] = { static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; +std::map Utils::listDirectory(const char *path) +{ + struct dirent de; + struct dirent *dptr; + std::map r; + + DIR *d = opendir(path); + if (!d) + return r; + + dptr = (struct dirent *)0; + for(;;) { + if (readdir_r(d,&de,&dptr)) + break; + if (dptr) { + if ((!strcmp(dptr->d_name,"."))&&(!strcmp(dptr->d_name,".."))) + r[std::string(dptr->d_name)] = (dptr->d_type == DT_DIR); + } else break; + } + + return r; +} + std::string Utils::base64Encode(const void *data,unsigned int len) { if (!len) diff --git a/node/Utils.hpp b/node/Utils.hpp index b1917b273..fbabe3d75 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "../ext/lz4/lz4.h" #include "../ext/lz4/lz4hc.h" @@ -58,6 +59,16 @@ namespace ZeroTier { class Utils { public: + /** + * List a directory's contents + * + * @param path Path to list + * @param files Set to fill with files + * @param directories Set to fill with directories + * @return Map of entries and whether or not they are also directories (empty on failure) + */ + static std::map listDirectory(const char *path); + /** * @param data Data to convert to hex * @param len Length of data