When processing events, AttachCurrentThread is called, but matching DetachCurrentThread was not being called This prevented main thread from exiting
305 lines
8.0 KiB
C++
305 lines
8.0 KiB
C++
/*
|
|
* Copyright (c)2013-2021 ZeroTier, Inc.
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file in the project's root directory.
|
|
*
|
|
* Change Date: 2026-01-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2.0 of the Apache License.
|
|
*/
|
|
/****/
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* Callback event creation and distribution to user application
|
|
*/
|
|
|
|
#include "Events.hpp"
|
|
|
|
#include "Mutex.hpp"
|
|
#include "NodeService.hpp"
|
|
#include "concurrentqueue.h"
|
|
|
|
#ifdef ZTS_ENABLE_JAVA
|
|
#include <jni.h>
|
|
#endif
|
|
|
|
#ifdef ZTS_ENABLE_PYTHON
|
|
#include "Python.h"
|
|
PythonDirectorCallbackClass* _userEventCallback = NULL;
|
|
void PythonDirectorCallbackClass::on_zerotier_event(zts_event_msg_t* msg)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
#define ZTS_NODE_EVENT(code) code >= ZTS_EVENT_NODE_UP&& code <= ZTS_EVENT_NODE_FATAL_ERROR
|
|
#define ZTS_NETWORK_EVENT(code) code >= ZTS_EVENT_NETWORK_NOT_FOUND&& code <= ZTS_EVENT_NETWORK_UPDATE
|
|
#define ZTS_STACK_EVENT(code) code >= ZTS_EVENT_STACK_UP&& code <= ZTS_EVENT_STACK_DOWN
|
|
#define ZTS_NETIF_EVENT(code) code >= ZTS_EVENT_NETIF_UP&& code <= ZTS_EVENT_NETIF_LINK_DOWN
|
|
#define ZTS_PEER_EVENT(code) code >= ZTS_EVENT_PEER_DIRECT&& code <= ZTS_EVENT_PEER_PATH_DEAD
|
|
#define ZTS_ROUTE_EVENT(code) code >= ZTS_EVENT_ROUTE_ADDED&& code <= ZTS_EVENT_ROUTE_REMOVED
|
|
#define ZTS_ADDR_EVENT(code) code >= ZTS_EVENT_ADDR_ADDED_IP4&& code <= ZTS_EVENT_ADDR_REMOVED_IP6
|
|
#define ZTS_STORE_EVENT(code) code >= ZTS_EVENT_STORE_IDENTITY_SECRET&& code <= ZTS_EVENT_STORE_NETWORK
|
|
|
|
namespace ZeroTier {
|
|
|
|
#ifdef ZTS_ENABLE_JAVA
|
|
// References to JNI objects and VM kept for future callbacks
|
|
JavaVM* jvm;
|
|
jobject javaCbObjRef = NULL;
|
|
jmethodID javaCbMethodId = NULL;
|
|
#endif
|
|
|
|
extern NodeService* zts_service;
|
|
|
|
// Global state variable shared between Socket, Control, Event and
|
|
// NodeService logic.
|
|
volatile uint8_t service_state = 0;
|
|
int last_state_check;
|
|
|
|
#define RESET_FLAGS() service_state = 0;
|
|
#define SET_FLAGS(f) service_state |= f;
|
|
#define CLR_FLAGS(f) service_state &= ~f;
|
|
#define GET_FLAGS(f) ((service_state & f) > 0)
|
|
|
|
// Lock to guard access to callback function pointers.
|
|
Mutex events_m;
|
|
|
|
#ifdef ZTS_ENABLE_PINVOKE
|
|
void (*_userEventCallback)(void*);
|
|
#endif
|
|
#ifdef ZTS_C_API_ONLY
|
|
void (*_userEventCallback)(void*);
|
|
#endif
|
|
|
|
moodycamel::ConcurrentQueue<zts_event_msg_t*> _callbackMsgQueue;
|
|
|
|
void Events::run()
|
|
{
|
|
while (getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) {
|
|
zts_event_msg_t* msg;
|
|
size_t sz = _callbackMsgQueue.size_approx();
|
|
for (size_t j = 0; j < sz; j++) {
|
|
if (_callbackMsgQueue.try_dequeue(msg)) {
|
|
events_m.lock();
|
|
sendToUser(msg);
|
|
events_m.unlock();
|
|
}
|
|
}
|
|
zts_util_delay(ZTS_CALLBACK_PROCESSING_INTERVAL);
|
|
}
|
|
}
|
|
|
|
void Events::enqueue(unsigned int event_code, const void* arg, int len)
|
|
{
|
|
if (! _enabled) {
|
|
return;
|
|
}
|
|
zts_event_msg_t* msg = new zts_event_msg_t();
|
|
msg->event_code = event_code;
|
|
|
|
if (ZTS_NODE_EVENT(event_code)) {
|
|
msg->node = (zts_node_info_t*)arg;
|
|
msg->len = sizeof(zts_node_info_t);
|
|
}
|
|
if (ZTS_NETWORK_EVENT(event_code)) {
|
|
msg->network = (zts_net_info_t*)arg;
|
|
msg->len = sizeof(zts_net_info_t);
|
|
}
|
|
if (ZTS_STACK_EVENT(event_code)) {
|
|
/* nothing to convey to user */
|
|
}
|
|
if (ZTS_NETIF_EVENT(event_code)) {
|
|
msg->netif = (zts_netif_info_t*)arg;
|
|
msg->len = sizeof(zts_netif_info_t);
|
|
}
|
|
if (ZTS_ROUTE_EVENT(event_code)) {
|
|
msg->route = (zts_route_info_t*)arg;
|
|
msg->len = sizeof(zts_route_info_t);
|
|
}
|
|
if (ZTS_PEER_EVENT(event_code)) {
|
|
msg->peer = (zts_peer_info_t*)arg;
|
|
msg->len = sizeof(zts_peer_info_t);
|
|
}
|
|
if (ZTS_ADDR_EVENT(event_code)) {
|
|
msg->addr = (zts_addr_info_t*)arg;
|
|
msg->len = sizeof(zts_addr_info_t);
|
|
}
|
|
if (ZTS_STORE_EVENT(event_code)) {
|
|
msg->cache = (void*)arg;
|
|
msg->len = len;
|
|
}
|
|
if (msg && _callbackMsgQueue.size_approx() > 1024) {
|
|
/* Rate-limit number of events. This value should only grow if the
|
|
user application isn't returning from the event handler in a timely manner.
|
|
For most applications it should hover around 1 to 2 */
|
|
destroy(msg);
|
|
}
|
|
else {
|
|
_callbackMsgQueue.enqueue(msg);
|
|
}
|
|
}
|
|
|
|
void Events::destroy(zts_event_msg_t* msg)
|
|
{
|
|
if (! msg) {
|
|
return;
|
|
}
|
|
if (msg->node) {
|
|
delete msg->node;
|
|
}
|
|
if (msg->network) {
|
|
delete msg->network;
|
|
}
|
|
if (msg->netif) {
|
|
delete msg->netif;
|
|
}
|
|
if (msg->route) {
|
|
delete msg->route;
|
|
}
|
|
if (msg->peer) {
|
|
delete msg->peer;
|
|
}
|
|
if (msg->addr) {
|
|
delete msg->addr;
|
|
}
|
|
delete msg;
|
|
msg = NULL;
|
|
}
|
|
|
|
void Events::sendToUser(zts_event_msg_t* msg)
|
|
{
|
|
bool bShouldStopCallbackThread = (msg->event_code == ZTS_EVENT_STACK_DOWN);
|
|
#ifdef ZTS_ENABLE_PYTHON
|
|
PyGILState_STATE state = PyGILState_Ensure();
|
|
_userEventCallback->on_zerotier_event(msg);
|
|
PyGILState_Release(state);
|
|
#endif
|
|
#ifdef ZTS_ENABLE_JAVA
|
|
if (javaCbMethodId) {
|
|
JNIEnv* env;
|
|
#if defined(__ANDROID__)
|
|
jvm->AttachCurrentThread(&env, NULL);
|
|
#else
|
|
jvm->AttachCurrentThread((void**)&env, NULL);
|
|
#endif
|
|
uint64_t id = 0;
|
|
if (ZTS_NODE_EVENT(msg->event_code)) {
|
|
id = msg->node ? msg->node->node_id : 0;
|
|
}
|
|
if (ZTS_NETWORK_EVENT(msg->event_code)) {
|
|
id = msg->network ? msg->network->net_id : 0;
|
|
}
|
|
if (ZTS_PEER_EVENT(msg->event_code)) {
|
|
id = msg->peer ? msg->peer->peer_id : 0;
|
|
}
|
|
env->CallVoidMethod(javaCbObjRef, javaCbMethodId, id, msg->event_code);
|
|
|
|
jvm->DetachCurrentThread();
|
|
}
|
|
#endif // ZTS_ENABLE_JAVA
|
|
#ifdef ZTS_ENABLE_PINVOKE
|
|
if (_userEventCallback) {
|
|
_userEventCallback(msg);
|
|
}
|
|
#endif
|
|
#ifdef ZTS_C_API_ONLY
|
|
if (_userEventCallback) {
|
|
_userEventCallback(msg);
|
|
}
|
|
#endif
|
|
destroy(msg);
|
|
if (bShouldStopCallbackThread) {
|
|
/* Ensure last possible callback ZTS_EVENT_STACK_DOWN is
|
|
delivered before callback thread is finally stopped. */
|
|
clrState(ZTS_STATE_CALLBACKS_RUNNING);
|
|
}
|
|
}
|
|
|
|
#ifdef ZTS_ENABLE_JAVA
|
|
void Events::setJavaCallback(jobject objRef, jmethodID methodId)
|
|
{
|
|
javaCbObjRef = objRef;
|
|
javaCbMethodId = methodId;
|
|
}
|
|
#endif
|
|
bool Events::hasCallback()
|
|
{
|
|
events_m.lock();
|
|
bool retval = false;
|
|
#ifdef ZTS_ENABLE_JAVA
|
|
retval = (jvm && javaCbObjRef && javaCbMethodId);
|
|
#else
|
|
retval = _userEventCallback;
|
|
#endif
|
|
events_m.unlock();
|
|
return retval;
|
|
}
|
|
|
|
void Events::clrCallback()
|
|
{
|
|
events_m.lock();
|
|
#ifdef ZTS_ENABLE_JAVA
|
|
javaCbObjRef = NULL;
|
|
javaCbMethodId = NULL;
|
|
#else
|
|
_userEventCallback = NULL;
|
|
#endif
|
|
events_m.unlock();
|
|
}
|
|
|
|
int Events::canPerformServiceOperation()
|
|
{
|
|
return zts_service && zts_service->isRunning() && ! getState(ZTS_STATE_FREE_CALLED);
|
|
}
|
|
|
|
void Events::setState(uint8_t newFlags)
|
|
{
|
|
if ((newFlags ^ service_state) & ZTS_STATE_NET_SERVICE_RUNNING) {
|
|
return; // No effect. Not allowed to set this flag manually
|
|
}
|
|
SET_FLAGS(newFlags);
|
|
if (GET_FLAGS(ZTS_STATE_NODE_RUNNING) && GET_FLAGS(ZTS_STATE_STACK_RUNNING)
|
|
&& ! (GET_FLAGS(ZTS_STATE_FREE_CALLED))) {
|
|
SET_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING);
|
|
}
|
|
else {
|
|
CLR_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING);
|
|
}
|
|
}
|
|
|
|
void Events::clrState(uint8_t newFlags)
|
|
{
|
|
if (newFlags & ZTS_STATE_NET_SERVICE_RUNNING) {
|
|
return; // No effect. Not allowed to set this flag manually
|
|
}
|
|
CLR_FLAGS(newFlags);
|
|
if (GET_FLAGS(ZTS_STATE_NODE_RUNNING) && GET_FLAGS(ZTS_STATE_STACK_RUNNING)
|
|
&& ! (GET_FLAGS(ZTS_STATE_FREE_CALLED))) {
|
|
SET_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING);
|
|
}
|
|
else {
|
|
CLR_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING);
|
|
}
|
|
}
|
|
|
|
bool Events::getState(uint8_t testFlags)
|
|
{
|
|
return testFlags & service_state;
|
|
}
|
|
|
|
void Events::enable()
|
|
{
|
|
_enabled = true;
|
|
}
|
|
|
|
void Events::disable()
|
|
{
|
|
_enabled = false;
|
|
}
|
|
|
|
} // namespace ZeroTier
|