diff --git a/Makefile.linux b/Makefile.linux index fef284748..c014ce9c7 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -24,12 +24,16 @@ LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -lm -ldl include objects.mk -all: one launcher +all: one cli launcher one: $(OBJS) $(CXX) $(CXXFLAGS) -o zerotier-one main.cpp $(OBJS) $(LIBS) $(STRIP) zerotier-one +cli: $(OBJS) + $(CXX) $(CXXFLAGS) -o zerotier-cli cli.cpp $(OBJS) $(LIBS) + $(STRIP) zerotier-cli + selftest: $(OBJS) $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.cpp $(OBJS) $(LIBS) $(STRIP) zerotier-selftest diff --git a/cli.cpp b/cli.cpp index a72a0890c..382377330 100644 --- a/cli.cpp +++ b/cli.cpp @@ -113,7 +113,7 @@ int main(int argc,char **argv) lastResultTime = Utils::now(); while ((Utils::now() - lastResultTime) < 300) - Thread::sleep(50); + Thread::sleep(50); if (!numResults) { fprintf(stdout,"ERROR: no results received. Is ZeroTier One running?"ZT_EOL_S); diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index c45d50c22..870baceba 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -187,7 +187,7 @@ EthernetTap::EthernetTap( TRACE("tap %s created",_dev); - start(); + _thread = Thread::start(this); } #endif // __LINUX__ @@ -271,14 +271,14 @@ EthernetTap::EthernetTap( ::pipe(_shutdownSignalPipe); - start(); + _thread = Thread::start(this); } #endif // __APPLE__ EthernetTap::~EthernetTap() { ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - join(); + Thread::join(_thread); ::close(_fd); } @@ -549,7 +549,7 @@ bool EthernetTap::updateMulticastGroups(std::set &groups) } #endif // __APPLE__ -void EthernetTap::main() +void EthernetTap::threadMain() throw() { fd_set readfds,nullfds; diff --git a/node/EthernetTap.hpp b/node/EthernetTap.hpp index 3bc7cad85..6fb4c1342 100644 --- a/node/EthernetTap.hpp +++ b/node/EthernetTap.hpp @@ -51,7 +51,7 @@ class RuntimeEnvironment; /** * System ethernet tap device */ -class EthernetTap : protected Thread +class EthernetTap { public: /** @@ -79,7 +79,7 @@ public: * * This may block for a few seconds while thread exits. */ - virtual ~EthernetTap(); + ~EthernetTap(); /** * Perform OS dependent actions on network configuration change detection @@ -169,8 +169,10 @@ public: */ bool updateMulticastGroups(std::set &groups); -protected: - virtual void main() + /** + * Thread main method; do not call elsewhere + */ + void threadMain() throw(); private: @@ -178,6 +180,7 @@ private: const unsigned int _mtu; const RuntimeEnvironment *_r; + Thread _thread; std::set _ips; Mutex _ips_m; diff --git a/node/Network.cpp b/node/Network.cpp index 37761affb..6deb31fe1 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -108,6 +108,8 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t id) _id(id), _lastConfigUpdate(0) { + if (controller() == _r->identity.address()) + throw std::runtime_error("configuration error: cannot add a network for which I am the netconf master"); } Network::~Network() @@ -118,6 +120,7 @@ void Network::setConfiguration(const Network::Config &conf) { Mutex::Lock _l(_lock); if ((conf.networkId() == _id)&&(conf.peerAddress() == _r->identity.address())) { // sanity check + TRACE("network %.16llx got netconf:\n%s",(unsigned long long)_id,conf.toString().c_str()); _configuration = conf; _myCertificate = conf.certificateOfMembership(); _lastConfigUpdate = Utils::now(); @@ -126,6 +129,11 @@ void Network::setConfiguration(const Network::Config &conf) void Network::requestConfiguration() { + if (controller() == _r->identity.address()) { + LOG("unable to request network configuration for network %.16llx: I am the network master, cannot query self",(unsigned long long)_id); + return; + } + TRACE("requesting netconf for network %.16llx from netconf master %s",(unsigned long long)_id,controller().toString().c_str()); Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append((uint64_t)_id); _r->sw->send(outp,true); diff --git a/node/Network.hpp b/node/Network.hpp index 4155d5d84..d3bb5ad7d 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -99,10 +99,10 @@ public: { } - /** - * @return Read-only underlying dictionary - */ - inline const Dictionary &dictionary() const { return *this; } + inline std::string toString() const + { + return Dictionary::toString(); + } inline void setNetworkId(uint64_t id) { @@ -208,11 +208,9 @@ public: { } - inline void setNetworkId(uint64_t id) + inline std::string toString() const { - char buf[32]; - sprintf(buf,"%.16llx",(unsigned long long)id); - (*this)["nwid"] = buf; + return Dictionary::toString(); } inline uint64_t networkId() const @@ -221,11 +219,6 @@ public: return strtoull(get("nwid").c_str(),(char **)0,16); } - inline void setPeerAddress(Address &a) - { - (*this)["peer"] = a.toString(); - } - inline Address peerAddress() const throw(std::invalid_argument) { @@ -265,41 +258,6 @@ public: sa.insert(InetAddress(*i)); return sa; } - - /** - * Set static IPv4 and IPv6 addresses - * - * This sets the ipv4Static and ipv6Static fields to comma-delimited - * lists of assignments. The port field in InetAddress must be the - * number of bits in the netmask. - * - * @param begin Start of container or array of addresses (InetAddress) - * @param end End of container or array of addresses (InetAddress) - * @tparam I Type of container or array - */ - template - inline void setStaticInetAddresses(const I &begin,const I &end) - { - std::string v4; - std::string v6; - for(I i(begin);i!=end;++i) { - if (i->isV4()) { - if (v4.length()) - v4.push_back(','); - v4.append(i->toString()); - } else if (i->isV6()) { - if (v6.length()) - v6.push_back(','); - v6.append(i->toString()); - } - } - if (v4.length()) - (*this)["ipv4Static"] = v4; - else erase("ipv4Static"); - if (v6.length()) - (*this)["ipv6Static"] = v6; - else erase("ipv6Static"); - } }; private: diff --git a/node/Node.cpp b/node/Node.cpp index ef2a801ca..da752bb27 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -426,7 +426,7 @@ Node::ReasonForTermination Node::run() if (lastDelayDelta >= ZT_SLEEP_WAKE_DETECTION_THRESHOLD) { resynchronize = true; LOG("probable suspend/resume detected, pausing a moment for things to settle..."); - Thread::sleep(ZT_SLEEP_WAKE_SETTLE_TIME); + Thread::sleep(ZT_SLEEP_WAKE_SETTLE_TIME); } // Periodically check our network environment, sending pings out to all diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 4a1745358..0a6c014f1 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -243,8 +243,10 @@ void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAdd for(std::vector< Buffer >::iterator p(resultPackets.begin());p!=resultPackets.end();++p) sock->send(remoteAddr,p->data(),p->size(),-1); } + } catch (std::exception &exc) { + TRACE("exception handling control bus packet from %s: %s",remoteAddr.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("exception handling control bus packet from %s",remoteAddr.toString().c_str()); + TRACE("exception handling control bus packet from %s: (unknown)",remoteAddr.toString().c_str()); } } diff --git a/node/Service.cpp b/node/Service.cpp index c614e4e40..0d4702f87 100644 --- a/node/Service.cpp +++ b/node/Service.cpp @@ -62,7 +62,7 @@ Service::Service(const RuntimeEnvironment *renv,const char *name,const char *pat _childStderr(0), _run(true) { - start(); + _thread = Thread::start(this); } Service::~Service() @@ -77,14 +77,14 @@ Service::~Service() pid = 0; break; } - Thread::sleep(100); + Thread::sleep(100); } if (pid > 0) { ::kill(pid,SIGKILL); waitpid(pid,&st,0); } } - join(); + Thread::join(_thread); } bool Service::send(const Dictionary &msg) @@ -107,7 +107,7 @@ bool Service::send(const Dictionary &msg) return true; } -void Service::main() +void Service::threadMain() throw() { char buf[131072]; @@ -136,7 +136,7 @@ void Service::main() close(in[0]); close(out[1]); close(err[1]); - Thread::sleep(500); // give child time to start + Thread::sleep(500); // give child time to start _childStdin = in[1]; _childStdout = out[0]; _childStderr = err[0]; @@ -168,7 +168,7 @@ void Service::main() LOG("service %s exited with exit code: %d, delaying 1s to attempt relaunch",_name.c_str(),st); - Thread::sleep(1000); // wait to relaunch + Thread::sleep(1000); // wait to relaunch continue; } } diff --git a/node/Service.hpp b/node/Service.hpp index c8b729c72..0b00d81df 100644 --- a/node/Service.hpp +++ b/node/Service.hpp @@ -60,7 +60,7 @@ class RuntimeEnvironment; * logged via the standard Logger instance. If the subprocess dies, an * attempt is made to restart it every second. */ -class Service : protected Thread +class Service { public: /** @@ -78,7 +78,7 @@ public: void (*handler)(void *,Service &,const Dictionary &), void *arg); - virtual ~Service(); + ~Service(); /** * Send a message to service subprocess @@ -106,12 +106,15 @@ public: return (_pid > 0); } -protected: - virtual void main() + /** + * Thread main method; do not call elsewhere + */ + void threadMain() throw(); private: const RuntimeEnvironment *_r; + Thread _thread; std::string _path; std::string _name; void *_arg; diff --git a/node/Thread.cpp b/node/Thread.cpp deleted file mode 100644 index f650f6fca..000000000 --- a/node/Thread.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 ZeroTier Networks LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include "Thread.hpp" - -#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) - -#include -#include -#include -#include -#include -#include - -extern "C" { -static void *__m_thread_main(void *ptr) -{ - ((ZeroTier::Thread *)ptr)->__intl_run(); - return (void *)0; -} -} - -namespace ZeroTier { - -Thread::Thread() : - _impl(malloc(sizeof(pthread_t))), - _running() -{ - memset(_impl,0,sizeof(pthread_t)); -} - -Thread::~Thread() -{ - free(_impl); -} - -void Thread::start() -{ - if (!*_running) { - ++_running; - pthread_create((pthread_t *)_impl,(const pthread_attr_t *)0,&__m_thread_main,(void *)this); - } -} - -void Thread::join() -{ - void *tmp; - if (*_running) - pthread_join(*((pthread_t *)_impl),&tmp); -} - -void Thread::sleep(unsigned long ms) -{ - usleep(ms * 1000); -} - -void Thread::__intl_run() -{ - for(;;) { - _notInit = false; - this->main(); - if (_notInit) // UGLY ASS HACK: see main() - usleep(50); - else break; - } - --_running; -} - -void Thread::main() - throw() -{ - _notInit = true; // UGLY ASS HACK: retry if subclass has not defined virtual function pointer yet -} - -} // namespace ZeroTier - -#endif - -#ifdef _WIN32 - -#include -#include -#include - -DWORD WINAPI __m_thread_main(LPVOID lpParam) -{ - ((ZeroTier::Thread *)lpParam)->__intl_run(); - return 0; -} - -struct __m_thread_info -{ - HANDLE threadHandle; - DWORD threadId; - bool started; -}; - -namespace ZeroTier { - -Thread::Thread() : - _impl(malloc(sizeof(__m_thread_info))), - _running() -{ - memset(_impl,0,sizeof(__m_thread_info)); -} - -Thread::~Thread() -{ - if (((__m_thread_info *)_impl)->started) - CloseHandle(((__m_thread_info *)_impl)->threadHandle); - free(_impl); -} - -void Thread::start() -{ - if (!*_running) { - ++_running; - if ((((__m_thread_info *)_impl)->threadHandle = CreateThread(NULL,0,__m_thread_main,this,0,&(((__m_thread_info *)_impl)->threadId))) != NULL) { - ((__m_thread_info *)_impl)->started = true; - } - } -} - -void Thread::join() -{ - if (*_running) - WaitForSingleObject(((__m_thread_info *)_impl)->threadHandle,INFINITE); -} - -void Thread::__intl_run() -{ - for(;;) { - _notInit = false; - this->main(); - if (_notInit) - Thread::sleep(50); - else break; - } - --_running; -} - -void Thread::main() - throw() -{ - _notInit = true; // HACK: retry if subclass has not defined virtual function pointer yet -} - -struct _Thread_RunInBackgroundData -{ - void (*func)(void *); - void *ptr; - HANDLE threadHandle; - DWORD threadId; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/Thread.hpp b/node/Thread.hpp index 9b399a00d..444da2be6 100644 --- a/node/Thread.hpp +++ b/node/Thread.hpp @@ -28,62 +28,104 @@ #ifndef _ZT_THREAD_HPP #define _ZT_THREAD_HPP -#include "NonCopyable.hpp" +#include + +#include "Constants.hpp" #include "AtomicCounter.hpp" +#ifdef __WINDOWS__ + +todo need windows; + +#else + +#include +#include +#include +#include +#include + namespace ZeroTier { +template +static void *___zt_threadMain(void *instance) +{ + try { + ((C *)instance)->threadMain(); + } catch ( ... ) {} + return (void *)0; +} + /** - * Wrapper for OS-dependent thread functions like pthread_create, etc. + * A thread of a given class type + * + * @tparam C Class using Thread */ -class Thread : NonCopyable +template +class Thread { public: - Thread(); - virtual ~Thread(); + Thread() + throw() + { + memset(&_tid,0,sizeof(_tid)); + } + + Thread(const Thread &t) + throw() + { + memcpy(&_tid,&(t._tid),sizeof(_tid)); + } + + inline Thread &operator=(const Thread &t) + throw() + { + memcpy(&_tid,&(t._tid),sizeof(_tid)); + return *this; + } /** - * Start thread -- can only be called once - */ - void start(); - - /** - * Wait for thread to terminate + * Start a new thread * - * More than one thread should not simultaneously use join(). + * @param instance Instance whose threadMain() method gets called by new thread + * @return Thread identifier + * @throws std::runtime_error Unable to create thread */ - void join(); + static inline Thread start(C *instance) + throw(std::runtime_error) + { + Thread t; + if (pthread_create(&t._tid,(const pthread_attr_t *)0,&___zt_threadMain,instance)) + throw std::runtime_error("pthread_create() failed, unable to create thread"); + return t; + } /** - * @return True if thread is running + * Join to a thread, waiting for it to terminate + * + * @param t Thread to join */ - inline bool running() const { return (*_running > 0); } - - /** - * Internal bounce method; do not call or override - */ - void __intl_run(); + static inline void join(const Thread &t) + { + pthread_join(t._tid,(void **)0); + } /** * Sleep the current thread * - * @param ms Milliseconds to sleep + * @param ms Number of milliseconds to sleep */ - static void sleep(unsigned long ms); - -protected: - /** - * Override to set a thread main function - */ - virtual void main() - throw(); + static inline void sleep(unsigned long ms) + { + usleep(ms * 1000); + } private: - void *_impl; - AtomicCounter _running; - volatile bool _notInit; + pthread_t _tid; }; } // namespace ZeroTier +#endif // __WINDOWS__ / !__WINDOWS__ + #endif diff --git a/node/Topology.cpp b/node/Topology.cpp index 8487684e3..ff012eaa3 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -38,7 +38,6 @@ namespace ZeroTier { Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath) throw(std::runtime_error) : - Thread(), _r(renv), _amSupernode(false) { @@ -55,7 +54,7 @@ Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath) Utils::lockDownFile(dbpath,false); // node.db caches secrets - start(); + _thread = Thread::start(this); } Topology::~Topology() @@ -68,10 +67,7 @@ Topology::~Topology() _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::EXIT_THREAD; } _peerDeepVerifyJobs_c.signal(); - - while (running()) - Thread::sleep(10); // wait for thread to terminate without join() - + Thread::join(_thread); KISSDB_close(&_dbm); } @@ -223,7 +219,7 @@ void Topology::clean() _peerDeepVerifyJobs_c.signal(); } -void Topology::main() +void Topology::threadMain() throw() { for(;;) { diff --git a/node/Topology.hpp b/node/Topology.hpp index 104ad344f..b917738e4 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -55,7 +55,7 @@ class RuntimeEnvironment; /** * Database of network topology */ -class Topology : protected Thread +class Topology { public: /** @@ -74,7 +74,7 @@ public: Topology(const RuntimeEnvironment *renv,const char *dbpath) throw(std::runtime_error); - virtual ~Topology(); + ~Topology(); /** * Set up supernodes for this network @@ -276,8 +276,10 @@ public: std::vector< SharedPtr > &_v; }; -protected: - virtual void main() + /** + * Thread main method; do not call elsewhere + */ + void threadMain() throw(); private: @@ -297,6 +299,7 @@ private: }; const RuntimeEnvironment *const _r; + Thread _thread; std::map< Address,SharedPtr > _activePeers; Mutex _activePeers_m; diff --git a/node/UdpSocket.cpp b/node/UdpSocket.cpp index 6178d16ea..db2c4b563 100644 --- a/node/UdpSocket.cpp +++ b/node/UdpSocket.cpp @@ -55,7 +55,6 @@ UdpSocket::UdpSocket( void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int), void *arg) throw(std::runtime_error) : - Thread(), _packetHandler(packetHandler), _arg(arg), _localPort(localPort), @@ -121,7 +120,7 @@ UdpSocket::UdpSocket( } } - start(); + _thread = Thread::start(this); } UdpSocket::~UdpSocket() @@ -146,7 +145,7 @@ bool UdpSocket::send(const InetAddress &to,const void *data,unsigned int len,int } } -void UdpSocket::main() +void UdpSocket::threadMain() throw() { char buf[32768]; diff --git a/node/UdpSocket.hpp b/node/UdpSocket.hpp index a3223f1f4..3f624387c 100644 --- a/node/UdpSocket.hpp +++ b/node/UdpSocket.hpp @@ -40,7 +40,7 @@ namespace ZeroTier { * * The socket listens in a background thread and sends packets to Switch. */ -class UdpSocket : protected Thread +class UdpSocket { public: /** @@ -61,7 +61,7 @@ public: void *arg) throw(std::runtime_error); - virtual ~UdpSocket(); + ~UdpSocket(); /** * @return Locally bound port @@ -87,11 +87,14 @@ public: bool send(const InetAddress &to,const void *data,unsigned int len,int hopLimit) throw(); -protected: - virtual void main() + /** + * Thread main method; do not call elsewhere + */ + void threadMain() throw(); private: + Thread _thread; void (*_packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int); void *_arg; int _localPort; diff --git a/objects.mk b/objects.mk index be5cdc4ef..87323b8c8 100644 --- a/objects.mk +++ b/objects.mk @@ -21,7 +21,6 @@ OBJS=\ node/Service.o \ node/Switch.o \ node/SysEnv.o \ - node/Thread.o \ node/Topology.o \ node/UdpSocket.o \ node/Utils.o