mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
made JsonApiServer an singleton through static method instance()
This commit is contained in:
parent
3b45fc5199
commit
d19d1685de
@ -44,6 +44,9 @@
|
|||||||
|
|
||||||
/*extern*/ JsonApiServer* jsonApiServer = nullptr;
|
/*extern*/ JsonApiServer* jsonApiServer = nullptr;
|
||||||
|
|
||||||
|
const std::string JsonApiServer::DEFAULT_LISTENING_ADDRESS = "127.0.0.1";
|
||||||
|
p3ConfigMgr *JsonApiServer::_config_mgr = nullptr;
|
||||||
|
|
||||||
/*static*/ const std::multimap<std::string, std::string>
|
/*static*/ const std::multimap<std::string, std::string>
|
||||||
JsonApiServer::corsHeaders =
|
JsonApiServer::corsHeaders =
|
||||||
{
|
{
|
||||||
@ -115,11 +118,33 @@ JsonApiServer::corsOptionsHeaders =
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonApiServer::JsonApiServer(uint16_t port, const std::string& bindAddress,
|
JsonApiServer& JsonApiServer::instance()
|
||||||
const std::function<bool(const std::string&)> newAccessRequestCallback ) :
|
{
|
||||||
mPort(port), mBindAddress(bindAddress),
|
static JsonApiServer *_instance = nullptr;
|
||||||
mNewAccessRequestCallback(newAccessRequestCallback),
|
|
||||||
configMutex("JsonApiServer config")
|
if(_instance == NULL)
|
||||||
|
{
|
||||||
|
_instance = new JsonApiServer();
|
||||||
|
|
||||||
|
if(_config_mgr == nullptr)
|
||||||
|
RsErr() << "JsonApiServer::instance() called before JsonApiServer::setConfigManager(). This is a bug!" << std::endl;
|
||||||
|
|
||||||
|
_config_mgr->addConfiguration("jsonapi.cfg",_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonApiServer::start(uint16_t port, const std::string& bindAddress, const std::function<bool(const std::string&)> callback)
|
||||||
|
{
|
||||||
|
mPort = port;
|
||||||
|
mBindAddress = bindAddress;
|
||||||
|
mNewAccessRequestCallback = callback;
|
||||||
|
|
||||||
|
RsThread::start("JsonApiServer");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config")
|
||||||
{
|
{
|
||||||
registerHandler("/rsLoginHelper/createLocation",
|
registerHandler("/rsLoginHelper/createLocation",
|
||||||
[this](const std::shared_ptr<rb::Session> session)
|
[this](const std::shared_ptr<rb::Session> session)
|
||||||
@ -342,6 +367,10 @@ JsonApiServer::JsonApiServer(uint16_t port, const std::string& bindAddress,
|
|||||||
} );
|
} );
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
RsFileHash dummyHash;
|
||||||
|
setFilename("jsonapi.cfg"); // hack
|
||||||
|
loadConfiguration(dummyHash);
|
||||||
|
|
||||||
// Generated at compile time
|
// Generated at compile time
|
||||||
#include "jsonapi-wrappers.inl"
|
#include "jsonapi-wrappers.inl"
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,11 @@ namespace rb = restbed;
|
|||||||
|
|
||||||
struct JsonApiServer;
|
struct JsonApiServer;
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Pointer to global instance of JsonApiServer
|
// * Pointer to global instance of JsonApiServer
|
||||||
* @jsonapi{development}
|
// * @jsonapi{development}
|
||||||
*/
|
// */
|
||||||
extern JsonApiServer* jsonApiServer;
|
// extern JsonApiServer* jsonApiServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple usage
|
* Simple usage
|
||||||
@ -53,20 +53,9 @@ extern JsonApiServer* jsonApiServer;
|
|||||||
struct JsonApiServer : RsSingleJobThread, p3Config
|
struct JsonApiServer : RsSingleJobThread, p3Config
|
||||||
{
|
{
|
||||||
static const uint16_t DEFAULT_PORT = 9092 ;
|
static const uint16_t DEFAULT_PORT = 9092 ;
|
||||||
|
static const std::string DEFAULT_LISTENING_ADDRESS ;
|
||||||
|
|
||||||
/**
|
static JsonApiServer& instance() ;
|
||||||
* @brief construct a JsonApiServer instance with given parameters
|
|
||||||
* @param[in] port listening port fpt the JSON API socket
|
|
||||||
* @param[in] bindAddress binding address for the JSON API socket
|
|
||||||
* @param newAccessRequestCallback called when a new auth token is asked to
|
|
||||||
* be authorized via JSON API, the auth token is passed as parameter, and
|
|
||||||
* the callback should return true if the new token get access granted and
|
|
||||||
* false otherwise, this usually requires user interacion to confirm access
|
|
||||||
*/
|
|
||||||
JsonApiServer(
|
|
||||||
uint16_t port = DEFAULT_PORT,
|
|
||||||
const std::string& bindAddress = "127.0.0.1",
|
|
||||||
const std::function<bool(const std::string&)> newAccessRequestCallback = [](const std::string&){return false;} );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param[in] path Path itno which publish the API call
|
* @param[in] path Path itno which publish the API call
|
||||||
@ -96,6 +85,17 @@ struct JsonApiServer : RsSingleJobThread, p3Config
|
|||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start
|
||||||
|
* Starts the json Api server.
|
||||||
|
*
|
||||||
|
* @param port port to listen to
|
||||||
|
* @param listen_address bind address to listen to
|
||||||
|
*/
|
||||||
|
void start( uint16_t port = DEFAULT_PORT,
|
||||||
|
const std::string& bindAddress = "127.0.0.1",
|
||||||
|
const std::function<bool(const std::string&)> newAccessRequestCallback = [](const std::string&){return false;});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function should be used by JSON API clients that aren't
|
* @brief This function should be used by JSON API clients that aren't
|
||||||
* authenticated yet, to ask their token to be authorized, the success or
|
* authenticated yet, to ask their token to be authorized, the success or
|
||||||
@ -175,10 +175,25 @@ struct JsonApiServer : RsSingleJobThread, p3Config
|
|||||||
static void version( uint32_t& major, uint32_t& minor, uint32_t& mini,
|
static void version( uint32_t& major, uint32_t& minor, uint32_t& mini,
|
||||||
std::string& extra, std::string&human );
|
std::string& extra, std::string&human );
|
||||||
|
|
||||||
|
|
||||||
|
static void setConfigMgr(p3ConfigMgr *cfg) { _config_mgr = cfg; }
|
||||||
|
protected:
|
||||||
/// @see RsSingleJobThread
|
/// @see RsSingleJobThread
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief construct a JsonApiServer instance with given parameters
|
||||||
|
* @param[in] port listening port fpt the JSON API socket
|
||||||
|
* @param[in] bindAddress binding address for the JSON API socket
|
||||||
|
* @param newAccessRequestCallback called when a new auth token is asked to
|
||||||
|
* be authorized via JSON API, the auth token is passed as parameter, and
|
||||||
|
* the callback should return true if the new token get access granted and
|
||||||
|
* false otherwise, this usually requires user interacion to confirm access
|
||||||
|
*/
|
||||||
|
JsonApiServer( );
|
||||||
|
|
||||||
|
|
||||||
/// @see p3Config::setupSerialiser
|
/// @see p3Config::setupSerialiser
|
||||||
virtual RsSerialiser* setupSerialiser();
|
virtual RsSerialiser* setupSerialiser();
|
||||||
|
|
||||||
@ -191,8 +206,8 @@ private:
|
|||||||
/// @see p3Config::saveDone
|
/// @see p3Config::saveDone
|
||||||
virtual void saveDone();
|
virtual void saveDone();
|
||||||
|
|
||||||
const uint16_t mPort;
|
uint16_t mPort;
|
||||||
const std::string mBindAddress;
|
std::string mBindAddress;
|
||||||
rb::Service mService;
|
rb::Service mService;
|
||||||
|
|
||||||
/// Called when new JSON API auth token is requested to be authorized
|
/// Called when new JSON API auth token is requested to be authorized
|
||||||
@ -220,5 +235,7 @@ private:
|
|||||||
return checkRsServicePtrReady(
|
return checkRsServicePtrReady(
|
||||||
serviceInstance.get(), serviceName, ctx, session );
|
serviceInstance.get(), serviceName, ctx, session );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static p3ConfigMgr *_config_mgr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,15 +173,16 @@ void p3ConfigMgr::addConfiguration(std::string file, pqiConfig *conf)
|
|||||||
}
|
}
|
||||||
// Also check that the filename is not already registered for another config
|
// Also check that the filename is not already registered for another config
|
||||||
|
|
||||||
for(std::list<pqiConfig*>::const_iterator it = mConfigs.begin(); it!= mConfigs.end(); ++it)
|
for(std::list<pqiConfig*>::iterator it = mConfigs.begin(); it!= mConfigs.end();)
|
||||||
if((*it)->filename == filename)
|
if((*it)->filename == filename)
|
||||||
{
|
{
|
||||||
std::cerr << "!!!!!!!!!! Trying to register a config for file \"" << filename << "\" that is already registered" << std::endl;
|
std::cerr << "(WW) Registering a config for file \"" << filename << "\" that is already registered. Replacing previous component." << std::endl;
|
||||||
std::cerr << "!!!!!!!!!! Please correct the code !" << std::endl;
|
it = mConfigs.erase(it);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
|
||||||
conf->setFilename(filename);
|
conf->setFilename(filename);// (cyril) this is quite terrible. The constructor of pqiConfig should take the filename as parameter and hold the information.
|
||||||
mConfigs.push_back(conf);
|
mConfigs.push_back(conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,13 +106,12 @@ void setHash(const RsFileHash& h);
|
|||||||
|
|
||||||
RsMutex cfgMtx;
|
RsMutex cfgMtx;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This sets the name of the pqi configuation file
|
* This sets the name of the pqi configuation file
|
||||||
*/
|
*/
|
||||||
void setFilename(const std::string& name);
|
void setFilename(const std::string& name);
|
||||||
|
|
||||||
|
private:
|
||||||
/**
|
/**
|
||||||
* @param an index for the Confind which contains list of configuarations that can be tracked
|
* @param an index for the Confind which contains list of configuarations that can be tracked
|
||||||
*/
|
*/
|
||||||
|
@ -91,7 +91,7 @@ void RsServer::rsGlobalShutDown()
|
|||||||
mNetMgr->shutdown(); /* Handles UPnP */
|
mNetMgr->shutdown(); /* Handles UPnP */
|
||||||
|
|
||||||
#ifdef RS_JSONAPI
|
#ifdef RS_JSONAPI
|
||||||
if(jsonApiServer) jsonApiServer->shutdown();
|
JsonApiServer::instance().shutdown();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rsAutoProxyMonitor::instance()->stopAllRSShutdown();
|
rsAutoProxyMonitor::instance()->stopAllRSShutdown();
|
||||||
|
@ -410,10 +410,8 @@ int RsInit::InitRetroShare(const RsConfigOptions& conf)
|
|||||||
|
|
||||||
#ifdef RS_JSONAPI
|
#ifdef RS_JSONAPI
|
||||||
if(rsInitConfig->jsonApiPort)
|
if(rsInitConfig->jsonApiPort)
|
||||||
{
|
JsonApiServer::instance().start(rsInitConfig->jsonApiPort, rsInitConfig->jsonApiBindAddress);
|
||||||
jsonApiServer = new JsonApiServer( rsInitConfig->jsonApiPort, rsInitConfig->jsonApiBindAddress );
|
|
||||||
jsonApiServer->start("JSON API Server");
|
|
||||||
}
|
|
||||||
#endif // ifdef RS_JSONAPI
|
#endif // ifdef RS_JSONAPI
|
||||||
|
|
||||||
return RS_INIT_OK;
|
return RS_INIT_OK;
|
||||||
@ -1214,14 +1212,7 @@ int RsServer::StartupRetroShare()
|
|||||||
//
|
//
|
||||||
mPluginsManager->loadPlugins(programatically_inserted_plugins) ;
|
mPluginsManager->loadPlugins(programatically_inserted_plugins) ;
|
||||||
|
|
||||||
#ifdef RS_JSONAPI
|
JsonApiServer::setConfigMgr(mConfigMgr);
|
||||||
if(jsonApiServer) // JsonApiServer may be disabled at runtime
|
|
||||||
{
|
|
||||||
mConfigMgr->addConfiguration("jsonApi.cfg", jsonApiServer);
|
|
||||||
RsFileHash dummyHash;
|
|
||||||
jsonApiServer->loadConfiguration(dummyHash);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**** Reputation system ****/
|
/**** Reputation system ****/
|
||||||
|
|
||||||
@ -1230,7 +1221,7 @@ int RsServer::StartupRetroShare()
|
|||||||
|
|
||||||
#ifdef RS_ENABLE_GXS
|
#ifdef RS_ENABLE_GXS
|
||||||
|
|
||||||
std::string currGxsDir = RsAccounts::AccountDirectory() + "/gxs";
|
std::string currGxsDir = RsAccounts::AccountDirectory() + "/gxs";
|
||||||
RsDirUtil::checkCreateDirectory(currGxsDir);
|
RsDirUtil::checkCreateDirectory(currGxsDir);
|
||||||
|
|
||||||
RsNxsNetMgr* nxsMgr = new RsNxsNetMgrImpl(serviceCtrl);
|
RsNxsNetMgr* nxsMgr = new RsNxsNetMgrImpl(serviceCtrl);
|
||||||
|
@ -83,7 +83,13 @@ void JsonApiPage::load()
|
|||||||
whileBlocking(ui.enableCheckBox)->setChecked(Settings->getJsonApiEnabled());
|
whileBlocking(ui.enableCheckBox)->setChecked(Settings->getJsonApiEnabled());
|
||||||
whileBlocking(ui.portSpinBox)->setValue(Settings->getJsonApiPort());
|
whileBlocking(ui.portSpinBox)->setValue(Settings->getJsonApiPort());
|
||||||
whileBlocking(ui.listenAddressLineEdit)->setText(Settings->getJsonApiListenAddress());
|
whileBlocking(ui.listenAddressLineEdit)->setText(Settings->getJsonApiListenAddress());
|
||||||
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(Settings->getJsonApiAuthTokens()));
|
|
||||||
|
QStringList newTk;
|
||||||
|
|
||||||
|
for(const auto& it : JsonApiServer::instance().getAuthorizedTokens())
|
||||||
|
newTk.push_back(QString::fromStdString(it.first)+":"+QString::fromStdString(it.second)) ;
|
||||||
|
|
||||||
|
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString JsonApiPage::helpText() const { return ""; }
|
QString JsonApiPage::helpText() const { return ""; }
|
||||||
@ -93,52 +99,38 @@ QString JsonApiPage::helpText() const { return ""; }
|
|||||||
checkShutdownJsonApi();
|
checkShutdownJsonApi();
|
||||||
|
|
||||||
if(Settings->getJsonApiEnabled())
|
if(Settings->getJsonApiEnabled())
|
||||||
{
|
JsonApiServer::instance().start( Settings->getJsonApiPort(), Settings->getJsonApiListenAddress().toStdString() );
|
||||||
jsonApiServer = new JsonApiServer(
|
|
||||||
Settings->getJsonApiPort(),
|
|
||||||
Settings->getJsonApiListenAddress().toStdString() );
|
|
||||||
jsonApiServer->start("jsonApiServer");
|
|
||||||
|
|
||||||
for(const QString& token : Settings->getJsonApiAuthTokens())
|
|
||||||
jsonApiServer->authorizeToken(token.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ void JsonApiPage::checkShutdownJsonApi()
|
/*static*/ void JsonApiPage::checkShutdownJsonApi()
|
||||||
{
|
{
|
||||||
if(jsonApiServer)
|
JsonApiServer::instance().shutdown();
|
||||||
{
|
|
||||||
/* It is important to make a copy of +jsonApiServer+ pointer so the old
|
/* It is important to make a copy of +jsonApiServer+ pointer so the old
|
||||||
* object can be deleted later, while the original pointer is
|
* object can be deleted later, while the original pointer is
|
||||||
* reassigned */
|
* reassigned */
|
||||||
JsonApiServer* oldJsonApiServer = jsonApiServer;
|
|
||||||
jsonApiServer = nullptr;
|
|
||||||
|
|
||||||
oldJsonApiServer->shutdown();
|
QProgressDialog* pd = new QProgressDialog(
|
||||||
|
"Stopping JSON API Server", QString(), 0, 3000);
|
||||||
|
QTimer* prtm = new QTimer;
|
||||||
|
prtm->setInterval(16); // 60 FPS
|
||||||
|
connect( prtm, &QTimer::timeout,
|
||||||
|
pd, [=](){pd->setValue(pd->value()+16);} );
|
||||||
|
pd->show();
|
||||||
|
prtm->start();
|
||||||
|
|
||||||
QProgressDialog* pd = new QProgressDialog(
|
/* Must wait for deletion because stopping of the server is async.
|
||||||
"Stopping JSON API Server", QString(), 0, 3000);
|
|
||||||
QTimer* prtm = new QTimer;
|
|
||||||
prtm->setInterval(16); // 60 FPS
|
|
||||||
connect( prtm, &QTimer::timeout,
|
|
||||||
pd, [=](){pd->setValue(pd->value()+16);} );
|
|
||||||
pd->show();
|
|
||||||
prtm->start();
|
|
||||||
|
|
||||||
/* Must wait for deletion because stopping of the server is async.
|
|
||||||
* It is important to capture a copy so it "survive" after
|
* It is important to capture a copy so it "survive" after
|
||||||
* safeStopJsonApiServer returns */
|
* safeStopJsonApiServer returns */
|
||||||
QTimer::singleShot(3*1000, [=]()
|
QTimer::singleShot(3*1000, [=]()
|
||||||
{
|
{
|
||||||
delete oldJsonApiServer;
|
prtm->stop();
|
||||||
prtm->stop();
|
pd->close();
|
||||||
pd->close();
|
prtm->deleteLater();
|
||||||
prtm->deleteLater();
|
pd->deleteLater();
|
||||||
pd->deleteLater();
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonApiPage::onApplyClicked(bool)
|
void JsonApiPage::onApplyClicked(bool)
|
||||||
@ -150,23 +142,27 @@ void JsonApiPage::onApplyClicked(bool)
|
|||||||
void JsonApiPage::addTokenClicked(bool)
|
void JsonApiPage::addTokenClicked(bool)
|
||||||
{
|
{
|
||||||
QString token(ui.tokenLineEdit->text());
|
QString token(ui.tokenLineEdit->text());
|
||||||
if(jsonApiServer) jsonApiServer->authorizeToken(token.toStdString());
|
JsonApiServer::instance().authorizeToken(token.toStdString());
|
||||||
QStringList newTk(Settings->getJsonApiAuthTokens());
|
|
||||||
newTk.removeAll(token);
|
QStringList newTk;
|
||||||
newTk.append(token);
|
|
||||||
Settings->setJsonApiAuthTokens(newTk);
|
for(const auto& it : JsonApiServer::instance().getAuthorizedTokens())
|
||||||
|
newTk.push_back(QString::fromStdString(it.first)+":"+QString::fromStdString(it.second)) ;
|
||||||
|
|
||||||
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk));
|
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonApiPage::removeTokenClicked(bool)
|
void JsonApiPage::removeTokenClicked(bool)
|
||||||
{
|
{
|
||||||
QString token(ui.tokenLineEdit->text());
|
QString token(ui.tokenLineEdit->text());
|
||||||
if(jsonApiServer) jsonApiServer->revokeAuthToken(token.toStdString());
|
JsonApiServer::instance().revokeAuthToken(token.toStdString());
|
||||||
QStringList newTk(Settings->getJsonApiAuthTokens());
|
|
||||||
newTk.removeAll(token);
|
QStringList newTk;
|
||||||
Settings->setJsonApiAuthTokens(newTk);
|
|
||||||
whileBlocking(ui.tokensListView)->setModel(
|
for(const auto& it : JsonApiServer::instance().getAuthorizedTokens())
|
||||||
new QStringListModel(Settings->getJsonApiAuthTokens()) );
|
newTk.push_back(QString::fromStdString(it.first)+":"+QString::fromStdString(it.second)) ;
|
||||||
|
|
||||||
|
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(Settings->getJsonApiAuthTokens()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonApiPage::tokenClicked(const QModelIndex& index)
|
void JsonApiPage::tokenClicked(const QModelIndex& index)
|
||||||
|
@ -30,6 +30,9 @@
|
|||||||
#include "rsharesettings.h"
|
#include "rsharesettings.h"
|
||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
|
|
||||||
|
// for this one, we'd rather use a file in retroshare/*h list.
|
||||||
|
#include <jsonapi/jsonapi.h>
|
||||||
|
|
||||||
#include <retroshare/rsnotify.h>
|
#include <retroshare/rsnotify.h>
|
||||||
#include <retroshare/rspeers.h>
|
#include <retroshare/rspeers.h>
|
||||||
|
|
||||||
@ -1208,7 +1211,7 @@ void RshareSettings::setJsonApiEnabled(bool enabled)
|
|||||||
|
|
||||||
uint16_t RshareSettings::getJsonApiPort()
|
uint16_t RshareSettings::getJsonApiPort()
|
||||||
{
|
{
|
||||||
return valueFromGroup("JsonApi", "port", 9092).toUInt();
|
return valueFromGroup("JsonApi", "port", JsonApiServer::DEFAULT_PORT).toUInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RshareSettings::setJsonApiPort(uint16_t port)
|
void RshareSettings::setJsonApiPort(uint16_t port)
|
||||||
|
@ -233,8 +233,6 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||||||
|
|
||||||
RsConfigOptions conf;
|
RsConfigOptions conf;
|
||||||
|
|
||||||
conf.jsonApiPort = 0 ; // disable JSon API at start. The JSonAPI preference UI will enable it according to saved parameters.
|
|
||||||
|
|
||||||
argstream as(argc,argv);
|
argstream as(argc,argv);
|
||||||
as >> option('s',"stderr" ,conf.outStderr ,"output to stderr instead of log file." )
|
as >> option('s',"stderr" ,conf.outStderr ,"output to stderr instead of log file." )
|
||||||
>> option('u',"udp" ,conf.udpListenerOnly,"Only listen to UDP." )
|
>> option('u',"udp" ,conf.udpListenerOnly,"Only listen to UDP." )
|
||||||
|
Loading…
Reference in New Issue
Block a user