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__
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__
#ifdef __WINDOWS__
@ -2012,13 +2013,13 @@ static void printHelp(const char *cn,FILE *out)
class _OneServiceRunner
{
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()
throw()
{
try {
for(;;) {
zt1Service = OneService::newInstance(homeDir.c_str(),port);
zt1Service = OneService::newInstance(homeDir.c_str(),port,scriptPath.c_str());
switch(zt1Service->run()) {
case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
case OneService::ONE_NORMAL_TERMINATION:
@ -2053,6 +2054,7 @@ public:
unsigned int returnValue;
unsigned int port;
const std::string &homeDir;
const std::string &scriptPath;
};
#ifdef __WINDOWS__
@ -2082,6 +2084,7 @@ int main(int argc,char **argv)
signal(SIGTERM,&_sighandlerQuit);
signal(SIGQUIT,&_sighandlerQuit);
signal(SIGINT,&_sighandlerQuit);
signal(SIGCHLD, SIG_IGN);
/* 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
@ -2115,7 +2118,7 @@ int main(int argc,char **argv)
if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI")))
return cli(argc,argv);
std::string homeDir;
std::string homeDir, scriptPath;
unsigned int port = ZT_DEFAULT_PORT;
bool skipRootCheck = false;
@ -2135,6 +2138,11 @@ int main(int argc,char **argv)
case 'd': // Run in background as daemon
runAsDaemon = true;
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__
case 'U':
@ -2306,7 +2314,7 @@ int main(int argc,char **argv)
}
#endif // __UNIX_LIKE__
_OneServiceRunner thr(argv[0],homeDir,port);
_OneServiceRunner thr(argv[0],homeDir,port,scriptPath);
thr.threadMain();
//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/Utils.hpp"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef __UNIX_LIKE__
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <dirent.h>
#include <netdb.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#ifdef __WINDOWS__
#include <windows.h>
#include <wincrypt.h>
#include <shlobj.h>
#include <netioapi.h>
#include <iphlpapi.h>
#include <netioapi.h>
#include <shlobj.h>
#include <wincrypt.h>
#include <windows.h>
#endif
#include "OSUtils.hpp"
@ -551,6 +551,53 @@ std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv)
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
// Used to convert HTTP header names to ASCII lower case

View file

@ -269,6 +269,16 @@ public:
*/
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
static nlohmann::json jsonParse(const std::string &buf);
static std::string jsonDump(const nlohmann::json &j,int indentation = 1);

View file

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

View file

@ -111,7 +111,7 @@ public:
* @param hp Home path
* @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();