9 changed files with 37 additions and 535 deletions
@ -1,222 +0,0 @@
|
||||
/*
|
||||
* ZeroTier One - Global Peer to Peer Ethernet |
||||
* Copyright (C) 2011-2014 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 <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* -- |
||||
* |
||||
* 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 "Constants.hpp" |
||||
|
||||
#ifndef __WINDOWS__ |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include <string.h> |
||||
#include <signal.h> |
||||
#include <time.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
#include <sys/time.h> |
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
#include <sys/select.h> |
||||
#include <sys/wait.h> |
||||
|
||||
#include "Constants.hpp" |
||||
#include "Service.hpp" |
||||
#include "RuntimeEnvironment.hpp" |
||||
#include "Utils.hpp" |
||||
#include "Logger.hpp" |
||||
|
||||
namespace ZeroTier { |
||||
|
||||
Service::Service(const RuntimeEnvironment *renv,const char *name,const char *path,void (*handler)(void *,Service &,const Dictionary &),void *arg) : |
||||
RR(renv), |
||||
_path(path), |
||||
_name(name), |
||||
_arg(arg), |
||||
_handler(handler), |
||||
_pid(-1), |
||||
_childStdin(0), |
||||
_childStdout(0), |
||||
_childStderr(0), |
||||
_run(true) |
||||
{ |
||||
_thread = Thread::start(this); |
||||
} |
||||
|
||||
Service::~Service() |
||||
{ |
||||
_run = false; |
||||
long pid = _pid; |
||||
if (pid > 0) { |
||||
int st = 0; |
||||
::kill(pid,SIGTERM); |
||||
for(int i=0;i<20;++i) { |
||||
if (waitpid(pid,&st,WNOHANG) == pid) { |
||||
pid = 0; |
||||
break; |
||||
} |
||||
Thread::sleep(100); |
||||
} |
||||
if (pid > 0) { |
||||
::kill(pid,SIGKILL); |
||||
waitpid(pid,&st,0); |
||||
} |
||||
} |
||||
Thread::join(_thread); |
||||
} |
||||
|
||||
bool Service::send(const Dictionary &msg) |
||||
{ |
||||
if (_childStdin <= 0) |
||||
return false; |
||||
std::string mser(msg.toString()); |
||||
mser.append(ZT_EOL_S); |
||||
return ((long)::write(_childStdin,mser.data(),mser.length()) == (long)mser.length()); |
||||
} |
||||
|
||||
void Service::threadMain() |
||||
throw() |
||||
{ |
||||
char buf[16384]; |
||||
fd_set readfds,writefds,exceptfds; |
||||
struct timeval tv; |
||||
int eolsInARow = 0; |
||||
std::string stderrBuf,stdoutBuf; |
||||
|
||||
while (_run) { |
||||
if (_pid <= 0) { |
||||
LOG("launching service %s...",_name.c_str()); |
||||
|
||||
int in[2],out[2],err[2]; |
||||
pipe(in); |
||||
pipe(out); |
||||
pipe(err); |
||||
|
||||
long pid = vfork(); |
||||
if (pid < 0) { |
||||
LOG("service %s terminating: could not fork!",_name.c_str()); |
||||
return; |
||||
} else if (pid) { |
||||
// Parent
|
||||
close(in[0]); |
||||
close(out[1]); |
||||
close(err[1]); |
||||
Thread::sleep(500); // give child time to start
|
||||
_childStdin = in[1]; |
||||
_childStdout = out[0]; |
||||
_childStderr = err[0]; |
||||
fcntl(_childStdout,F_SETFL,O_NONBLOCK); |
||||
fcntl(_childStderr,F_SETFL,O_NONBLOCK); |
||||
_pid = pid; |
||||
} else { |
||||
// Child
|
||||
close(in[1]); |
||||
close(out[0]); |
||||
close(err[0]); |
||||
dup2(in[0],STDIN_FILENO); |
||||
dup2(out[1],STDOUT_FILENO); |
||||
dup2(err[1],STDERR_FILENO); |
||||
setenv("ZT_HOME",RR->homePath.c_str(),1); |
||||
chdir(RR->homePath.c_str()); |
||||
execl(_path.c_str(),_path.c_str(),RR->homePath.c_str(),(const char *)0); |
||||
exit(-1); |
||||
} |
||||
} else { |
||||
int st = 0; |
||||
if (waitpid(_pid,&st,WNOHANG) == _pid) { |
||||
if (_childStdin > 0) close(_childStdin); |
||||
_childStdin = 0; |
||||
if (_childStdout > 0) close(_childStdout); |
||||
if (_childStderr > 0) close(_childStderr); |
||||
_pid = 0; |
||||
|
||||
if (!_run) |
||||
return; |
||||
|
||||
LOG("service %s exited with exit code: %d, delaying 1s to attempt relaunch",_name.c_str(),st); |
||||
|
||||
Thread::sleep(1000); // wait to relaunch
|
||||
continue; |
||||
} |
||||
} |
||||
|
||||
// If we've made it here, _pid is running last we checked.
|
||||
|
||||
FD_ZERO(&readfds); |
||||
FD_ZERO(&writefds); |
||||
FD_ZERO(&exceptfds); |
||||
|
||||
FD_SET(_childStdout,&readfds); |
||||
FD_SET(_childStderr,&readfds); |
||||
|
||||
tv.tv_sec = 1; |
||||
tv.tv_usec = 0; |
||||
::select(std::max(_childStdout,_childStderr)+1,&readfds,&writefds,&exceptfds,&tv); |
||||
|
||||
if (!_run) { |
||||
if (_childStdin > 0) ::close(_childStdin); |
||||
_childStdin = 0; |
||||
if (_childStdout > 0) ::close(_childStdout); |
||||
if (_childStderr > 0) ::close(_childStderr); |
||||
return; |
||||
} |
||||
|
||||
if ((_childStderr > 0)&&(FD_ISSET(_childStderr,&readfds))) { |
||||
int n = (int)::read(_childStderr,buf,sizeof(buf)); |
||||
for(int i=0;i<n;++i) { |
||||
if ((buf[i] == '\r')||(buf[i] == '\n')) { |
||||
stderrBuf = Utils::trim(stderrBuf); |
||||
if (stderrBuf.length()) |
||||
LOG("service %s: %s",_name.c_str(),stderrBuf.c_str()); |
||||
stderrBuf = ""; |
||||
} else stderrBuf.push_back(buf[i]); |
||||
} |
||||
} |
||||
|
||||
if ((_childStdout > 0)&&(FD_ISSET(_childStdout,&readfds))) { |
||||
int n = (int)::read(_childStdout,buf,sizeof(buf)); |
||||
for(int i=0;i<n;++i) { |
||||
if ((buf[i] == '\n')||(buf[i] == '\r')) { |
||||
if (buf[i] == '\n') |
||||
++eolsInARow; |
||||
} else eolsInARow = 0; |
||||
|
||||
if (eolsInARow >= 2) { |
||||
// Two CRs in a row ends a message
|
||||
try { |
||||
_handler(_arg,*this,Dictionary(stdoutBuf)); |
||||
stdoutBuf = ""; |
||||
} catch ( ... ) {} // handlers should not throw
|
||||
} else stdoutBuf.push_back(buf[i]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // __WINDOWS__
|
||||
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* ZeroTier One - Global Peer to Peer Ethernet |
||||
* Copyright (C) 2011-2014 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 <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* -- |
||||
* |
||||
* 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/
|
||||
*/ |
||||
|
||||
#ifndef ZT_SERVICE_HPP |
||||
#define ZT_SERVICE_HPP |
||||
|
||||
#include <string> |
||||
#include <stdexcept> |
||||
|
||||
#include "Constants.hpp" |
||||
#include "Dictionary.hpp" |
||||
#include "Thread.hpp" |
||||
|
||||
namespace ZeroTier { |
||||
|
||||
class RuntimeEnvironment; |
||||
|
||||
#ifndef __WINDOWS__ |
||||
/**
|
||||
* A subprocess that communicates with the host via a simple protocol |
||||
* |
||||
* This is currently only supported on *nix systems, and is used to implement |
||||
* special plugins that are used by supernodes and network configuration |
||||
* master nodes. Users will probably have no use for it. |
||||
* |
||||
* The simple binary protocol consists of a bidirectional stream of string- |
||||
* serialized Dictionaries prefixed by a 32-bit message length. Input |
||||
* messages are sent to the subprocess via its stdin, and output is read |
||||
* from its stdout. Messages printed by the subprocess on its stderr are |
||||
* logged via the standard Logger instance. If the subprocess dies, an |
||||
* attempt is made to restart it every second. |
||||
*/ |
||||
class Service |
||||
{ |
||||
public: |
||||
/**
|
||||
* Create and launch a new service |
||||
* |
||||
* @param renv Runtime environment |
||||
* @param name Name of service |
||||
* @param path Path to service binary |
||||
* @param handler Handler function to call when service generates output |
||||
* @param arg First argument to service |
||||
*/ |
||||
Service(const RuntimeEnvironment *renv, |
||||
const char *name, |
||||
const char *path, |
||||
void (*handler)(void *,Service &,const Dictionary &), |
||||
void *arg); |
||||
|
||||
~Service(); |
||||
|
||||
/**
|
||||
* Send a message to service subprocess |
||||
* |
||||
* @param msg Message in key/value dictionary form |
||||
* @return True if message was sent |
||||
*/ |
||||
bool send(const Dictionary &msg); |
||||
|
||||
/**
|
||||
* @return Name of service |
||||
*/ |
||||
inline const char *name() const throw() { return _name.c_str(); } |
||||
|
||||
/**
|
||||
* @return True if subprocess is running |
||||
*/ |
||||
inline bool running() const throw() { return (_pid > 0); } |
||||
|
||||
/**
|
||||
* Thread main method; do not call elsewhere |
||||
*/ |
||||
void threadMain() |
||||
throw(); |
||||
|
||||
private: |
||||
const RuntimeEnvironment *RR; |
||||
|
||||
Thread _thread; |
||||
|
||||
std::string _path; |
||||
std::string _name; |
||||
void *_arg; |
||||
void (*_handler)(void *,Service &,const Dictionary &); |
||||
volatile long _pid; |
||||
|
||||
volatile int _childStdin; |
||||
volatile int _childStdout; |
||||
volatile int _childStderr; |
||||
|
||||
volatile bool _run; |
||||
}; |
||||
#endif // __WINDOWS__
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif |
||||
Loading…
Reference in new issue