add network event script support

Based on PR #1669 of @joseph-henry

Co-authored-by: Joseph Henry
Signed-off-by: Oskari Rauta <oskari.rauta@gmail.com>
This commit is contained in:
Oskari Rauta 2023-01-27 15:59:27 +00:00
commit da0ed006e8
5 changed files with 96 additions and 22 deletions

16
one.cpp
View file

@ -1996,6 +1996,7 @@ static void printHelp(const char *cn,FILE *out)
#ifdef __UNIX_LIKE__ #ifdef __UNIX_LIKE__
fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)" ZT_EOL_S); fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)" ZT_EOL_S);
fprintf(out," -s<path> - path to script to execute on network events" ZT_EOL_S);
#endif // __UNIX_LIKE__ #endif // __UNIX_LIKE__
#ifdef __WINDOWS__ #ifdef __WINDOWS__
@ -2012,13 +2013,13 @@ static void printHelp(const char *cn,FILE *out)
class _OneServiceRunner class _OneServiceRunner
{ {
public: public:
_OneServiceRunner(const char *pn,const std::string &hd,unsigned int p) : progname(pn),returnValue(0),port(p),homeDir(hd) {} _OneServiceRunner(const char *pn,const std::string &hd,unsigned int p, std::string &sp) : progname(pn),returnValue(0),port(p),homeDir(hd),scriptPath(sp) {}
void threadMain() void threadMain()
throw() throw()
{ {
try { try {
for(;;) { for(;;) {
zt1Service = OneService::newInstance(homeDir.c_str(),port); zt1Service = OneService::newInstance(homeDir.c_str(),port,scriptPath.c_str());
switch(zt1Service->run()) { switch(zt1Service->run()) {
case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
case OneService::ONE_NORMAL_TERMINATION: case OneService::ONE_NORMAL_TERMINATION:
@ -2053,6 +2054,7 @@ public:
unsigned int returnValue; unsigned int returnValue;
unsigned int port; unsigned int port;
const std::string &homeDir; const std::string &homeDir;
const std::string &scriptPath;
}; };
#ifdef __WINDOWS__ #ifdef __WINDOWS__
@ -2082,6 +2084,7 @@ int main(int argc,char **argv)
signal(SIGTERM,&_sighandlerQuit); signal(SIGTERM,&_sighandlerQuit);
signal(SIGQUIT,&_sighandlerQuit); signal(SIGQUIT,&_sighandlerQuit);
signal(SIGINT,&_sighandlerQuit); signal(SIGINT,&_sighandlerQuit);
signal(SIGCHLD, SIG_IGN);
/* Ensure that there are no inherited file descriptors open from a previous /* Ensure that there are no inherited file descriptors open from a previous
* incarnation. This is a hack to ensure that GitHub issue #61 or variants * incarnation. This is a hack to ensure that GitHub issue #61 or variants
@ -2115,7 +2118,7 @@ int main(int argc,char **argv)
if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI"))) if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI")))
return cli(argc,argv); return cli(argc,argv);
std::string homeDir; std::string homeDir, scriptPath;
unsigned int port = ZT_DEFAULT_PORT; unsigned int port = ZT_DEFAULT_PORT;
bool skipRootCheck = false; bool skipRootCheck = false;
@ -2135,6 +2138,11 @@ int main(int argc,char **argv)
case 'd': // Run in background as daemon case 'd': // Run in background as daemon
runAsDaemon = true; runAsDaemon = true;
break; break;
case 's': // script to run on network events
if (argv[i][2]) {
scriptPath = argv[i] + 2;
} else printHelp(argv[0],stdout);
break;
#endif // __UNIX_LIKE__ #endif // __UNIX_LIKE__
case 'U': case 'U':
@ -2306,7 +2314,7 @@ int main(int argc,char **argv)
} }
#endif // __UNIX_LIKE__ #endif // __UNIX_LIKE__
_OneServiceRunner thr(argv[0],homeDir,port); _OneServiceRunner thr(argv[0],homeDir,port,scriptPath);
thr.threadMain(); thr.threadMain();
//Thread::join(Thread::start(&thr)); //Thread::join(Thread::start(&thr));

View file

@ -11,34 +11,34 @@
*/ */
/****/ /****/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef __UNIX_LIKE__ #ifdef __UNIX_LIKE__
#include <unistd.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/types.h> #include <netdb.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <dirent.h> #include <sys/wait.h>
#include <netdb.h> #include <unistd.h>
#endif #endif
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <windows.h>
#include <wincrypt.h>
#include <shlobj.h>
#include <netioapi.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#include <netioapi.h>
#include <shlobj.h>
#include <wincrypt.h>
#include <windows.h>
#endif #endif
#include "OSUtils.hpp" #include "OSUtils.hpp"
@ -551,6 +551,53 @@ std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv)
return std::string(); return std::string();
} }
void OSUtils::_hookCmd(const char* scriptPath, const uint64_t nwid, const char *homePath, ZT_VirtualNetworkConfigOperation op)
{
if (! scriptPath || ! strlen(scriptPath) || nwid == 0) {
#ifdef ZT_TRACE
fprintf(stderr, "no script path specified\n");
#endif
return;
}
long p = (long)fork();
if (p > 0) {
#ifdef ZT_TRACE
fprintf(stderr, "Running network event hook script (%s)\n", scriptPath);
#endif
//int exitcode = -1;
//::waitpid(p, &exitcode, 0);
}
else if (p == 0) {
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
char cmdBuf[128] = { 0 };
OSUtils::ztsnprintf(cmdBuf, sizeof(cmdBuf), "%s", scriptPath);
char nwidStr[17] = { 0 };
OSUtils::ztsnprintf(nwidStr, sizeof(nwidStr), "%.16llx", nwid);
std::string cmd;
switch (op) {
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
cmd = std::string(cmdBuf) + " network_up " + std::string(nwidStr) + " " + std::string(homePath) + "";
//::execl("/bin/sh", "sh", cmdBuf, "network_up", nwidStr, homePath, "&", (const char*)0);
break;
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
cmd = std::string(cmdBuf) + " network_update " + std::string(nwidStr) + " " + std::string(homePath) + "";
//::execl("/bin/sh", "sh", cmdBuf, "network_update", nwidStr, homePath, "&", (const char*)0);
break;
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN: // Same as _DESTROY
cmd = std::string(cmdBuf) + " network_down " + std::string(nwidStr) + " " + std::string(homePath) + "";
//::execl("/bin/sh", "sh", cmdBuf, "network_down", nwidStr, homePath, "&", (const char*)0);
break;
default:
break;
}
// Using system instead of execl, because execl blocks connections through cli
if (!cmd.empty()) ::system(cmd.c_str());
::_exit(-1);
}
}
#endif // OMIT_JSON_SUPPORT #endif // OMIT_JSON_SUPPORT
// Used to convert HTTP header names to ASCII lower case // Used to convert HTTP header names to ASCII lower case

View file

@ -269,6 +269,16 @@ public:
*/ */
static std::string platformDefaultHomePath(); static std::string platformDefaultHomePath();
/**
* Executes a system script specified by user
*
* @param scriptPath The full path to the script that should be run on each network event
* @param nwid The Network ID of the network in question
* @param homePath full patch to homepath containing connections settings and data
* @param op The operation performed up on the network
*/
static void _hookCmd(const char *scriptPath, const uint64_t nwid, const char *homePath, ZT_VirtualNetworkConfigOperation op);
#ifndef OMIT_JSON_SUPPORT #ifndef OMIT_JSON_SUPPORT
static nlohmann::json jsonParse(const std::string &buf); static nlohmann::json jsonParse(const std::string &buf);
static std::string jsonDump(const nlohmann::json &j,int indentation = 1); static std::string jsonDump(const nlohmann::json &j,int indentation = 1);

View file

@ -704,8 +704,11 @@ public:
// begin member variables -------------------------------------------------- // begin member variables --------------------------------------------------
const std::string _homePath; const std::string _homePath;
std::string _authToken; std::string _authToken;
std::string _controllerDbPath; std::string _controllerDbPath;
std::string _eventHookScriptPath;
const std::string _networksPath; const std::string _networksPath;
const std::string _moonsPath; const std::string _moonsPath;
@ -804,9 +807,10 @@ public:
// end member variables ---------------------------------------------------- // end member variables ----------------------------------------------------
OneServiceImpl(const char *hp,unsigned int port) : OneServiceImpl(const char *hp,unsigned int port,const char *sp) :
_homePath((hp) ? hp : ".") _homePath((hp) ? hp : ".")
,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S "controller.d") ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S "controller.d")
,_eventHookScriptPath(sp)
,_networksPath(_homePath + ZT_PATH_SEPARATOR_S "networks.d") ,_networksPath(_homePath + ZT_PATH_SEPARATOR_S "networks.d")
,_moonsPath(_homePath + ZT_PATH_SEPARATOR_S "moons.d") ,_moonsPath(_homePath + ZT_PATH_SEPARATOR_S "moons.d")
,_controller((EmbeddedNetworkController *)0) ,_controller((EmbeddedNetworkController *)0)
@ -2160,6 +2164,10 @@ public:
} }
#endif #endif
if (_eventHookScriptPath.empty()) {
_eventHookScriptPath = std::string(OSUtils::jsonString(settings["eventHookScriptPath"],""));
}
json &ignoreIfs = settings["interfacePrefixBlacklist"]; json &ignoreIfs = settings["interfacePrefixBlacklist"];
if (ignoreIfs.is_array()) { if (ignoreIfs.is_array()) {
for(unsigned long i=0;i<ignoreIfs.size();++i) { for(unsigned long i=0;i<ignoreIfs.size();++i) {
@ -2796,6 +2804,7 @@ public:
} }
break; break;
} }
OSUtils::_hookCmd(_eventHookScriptPath.c_str(), nwid, _homePath.c_str(), op);
return 0; return 0;
} }
@ -3555,7 +3564,7 @@ std::string OneService::platformDefaultHomePath()
return OSUtils::platformDefaultHomePath(); return OSUtils::platformDefaultHomePath();
} }
OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); } OneService *OneService::newInstance(const char *hp,unsigned int port,const char *sp) { return new OneServiceImpl(hp,port,sp); }
OneService::~OneService() {} OneService::~OneService() {}
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -111,7 +111,7 @@ public:
* @param hp Home path * @param hp Home path
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port) * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
*/ */
static OneService *newInstance(const char *hp,unsigned int port); static OneService *newInstance(const char *hp,unsigned int port, const char *sp);
virtual ~OneService(); virtual ~OneService();