/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#pragma warning( disable : 4786 )
#ifdef _WIN32
//#define ESP_SUPPORT_DALI_CONFIG
//CRT
#include
#endif
//Jlib
#include "jliball.hpp"
//SCM Interfaces
#include "esp.hpp"
#include "espplugin.hpp"
#include "espplugin.ipp"
#include "espcfg.ipp"
#include "xslprocessor.hpp"
#include "espcontext.hpp"
#include
/*
#if defined(USING_MPATROL)
#define ESP_BUILTIN
#endif
*/
//#define ESP_BUILTIN
extern "C" {
ESP_FACTORY IEspService * esp_service_factory(const char *name, const char* type, IPropertyTree *cfg, const char *process);
ESP_FACTORY IEspRpcBinding * esp_binding_factory(const char *name, const char* type, IPropertyTree *cfg, const char *process);
ESP_FACTORY IEspProtocol * esp_protocol_factory(const char *name, const char* type, IPropertyTree *cfg, const char *process);
};
#ifdef ESP_BUILTIN
builtin espdirect;
#endif
// add suffix and prefix when necessary
void fixPlugin(StringBuffer& plugin)
{
if (stricmp(plugin.str()+plugin.length()-sizeof(SharedObjectExtension)+1,SharedObjectExtension)==0)
return;
plugin.insert(0,SharedObjectPrefix);
plugin.append(SharedObjectExtension);
}
void CEspConfig::loadBuiltIns()
{
#ifdef ESP_BUILTIN
espdirect.prot = esp_protocol_factory;
espdirect.bind = esp_binding_factory;
espdirect.serv = esp_service_factory;
#endif
}
builtin *CEspConfig::getBuiltIn(string name)
{
#ifdef ESP_BUILTIN
//if (name.compare("pixall.dll")==0 || name.compare("pixall.so")==0)
return &espdirect;
#else //ESP_DIRECT
return NULL;
#endif
}
StringBuffer &CVSBuildToEspVersion(char const * tag, StringBuffer & out)
{
unsigned build = 0;
unsigned subbuild = 0;
while(!isdigit(*tag))
{
if(!*tag) break;
tag++;
}
while(isdigit(*tag))
{
if(!*tag) break;
build = 10*build + (*tag-'0');
tag++;
}
if(isalpha(*tag))
{
if(islower(*tag))
subbuild = *tag-'a'+1;
else
subbuild = *tag-'A'+1;
}
out.append(build/10).append('.').append(build%10).append(subbuild);
return out;
}
CEspConfig::CEspConfig(IProperties* inputs, IPropertyTree* envpt, IPropertyTree* procpt, bool isDali)
{
hsami_=0;
serverstatus=NULL;
useDali=false;
if(inputs)
m_inputs.setown(inputs);
if(!envpt || !procpt)
return;
m_envpt.setown(envpt);
m_cfg.setown(procpt);
loadBuiltIns();
// load options
const char* level = m_cfg->queryProp("@logLevel");
m_options.logLevel = level ? atoi(level) : LogMin;
m_options.logReq = m_cfg->getPropBool("@logRequests", false);
m_options.logResp = m_cfg->getPropBool("@logResponses", false);
m_options.frameTitle.set(m_cfg->queryProp("@name"));
m_options.slowProcessingTime = m_cfg->getPropInt("@slowProcessingTime", 30) * 1000; //in msec
#ifdef USE_ENV_CONF_FILE
// load environment parameters
StringBuffer envConfFile, envXMLFile;
const char* configFile = m_cfg->queryProp("EnvironmentConfFile");
if (configFile && *configFile)
{
envConfFile.append(configFile);
}
else
{
envConfFile.append("/etc/LexisNexis/environment.conf");
}
const char* envFromDali = m_cfg->queryProp("@environmentNotFromDali");
if (envFromDali && !stricmp(envFromDali, "true"))
{
const char* envXML = m_cfg->queryProp("EnvironmentXMLFile");
if (envXML && *envXML)
{
envXMLFile.append(envXML);
}
else
{
envXMLFile.append("environment.xml");
}
}
Owned factory = getEnvironmentFactory();
factory->createEnvironmentByFile(envConfFile.str(), envXMLFile.str());
#endif
if (!m_cfg->getProp("@name", m_process))
{
ERRLOG("EspProcess name not found");
}
else
{
DBGLOG("ESP process name [%s]", m_process.str());
IPropertyTreeIterator *pt_iter = NULL;
StringBuffer daliservers;
if (m_cfg->getProp("@daliServers", daliservers))
initDali(daliservers.str());
#ifndef _DEBUG
startPerformanceMonitor(m_cfg->getPropInt("@perfReportDelay", 60)*1000);
#endif
//get the local computer name:
m_cfg->getProp("@computer", m_computer);
//get the local computer information:
StringBuffer xpath;
xpath.appendf("Hardware/Computer[@name=\"%s\"]", m_computer.str());
IPropertyTree *computer = m_envpt->queryPropTree(xpath.str());
if (computer)
{
StringBuffer address;
computer->getProp("@netAddress", address);
int port = m_cfg->getPropInt("@port", 1500);
if(strcmp(address.str(), ".") == 0)
{
GetHostName(address.clear());
}
m_address.set(address.str(), (unsigned short) port);
}
xpath.clear();
xpath.append("EspService");
pt_iter = m_cfg->getElements(xpath.str());
if (pt_iter!=NULL)
{
IPropertyTree *ptree = NULL;
pt_iter->first();
while(pt_iter->isValid())
{
ptree = &pt_iter->query();
if (ptree)
{
srv_cfg *svcfg = new srv_cfg;
ptree->getProp("@name", svcfg->name);
ptree->getProp("@type", svcfg->type);
ptree->getProp("@plugin", svcfg->plugin);
fixPlugin(svcfg->plugin);
map::value_type en(svcfg->name.str(), svcfg);
m_services.insert(en);
}
pt_iter->next();
}
pt_iter->Release();
pt_iter=NULL;
}
xpath.clear();
xpath.append("EspProtocol");
pt_iter = m_cfg->getElements(xpath.str());
if (pt_iter!=NULL)
{
IPropertyTree *ptree = NULL;
pt_iter->first();
while(pt_iter->isValid())
{
ptree = &pt_iter->query();
if (ptree)
{
protocol_cfg *pcfg = new protocol_cfg;
ptree->getProp("@name", pcfg->name);
ptree->getProp("@plugin", pcfg->plugin);
fixPlugin(pcfg->plugin);
ptree->getProp("@type", pcfg->type);
map::value_type en(pcfg->name.str(), pcfg);
m_protocols.insert(en);
}
pt_iter->next();
}
pt_iter->Release();
pt_iter=NULL;
}
xpath.clear();
xpath.append("EspBinding");
pt_iter = m_cfg->getElements(xpath.str());
if (pt_iter!=NULL)
{
IPropertyTree *ptree = NULL;
pt_iter->first();
while(pt_iter->isValid())
{
ptree = &pt_iter->query();
if (ptree)
{
binding_cfg *bcfg = new binding_cfg;
ptree->getProp("@name", bcfg->name);
ptree->getProp("@type", bcfg->type);
ptree->getProp("@plugin", bcfg->plugin);
fixPlugin(bcfg->plugin);
bcfg->isDefault = ptree->getPropBool("@defaultBinding", false);
StringBuffer addr;
ptree->getProp("@netAddress", addr);
if(strcmp(addr.str(), ".") == 0)
{
bcfg->address.append("0.0.0.0");
}
else
{
bcfg->address.append(addr.str());
}
StringBuffer portstr;
ptree->getProp("@port", portstr);
bcfg->port = atoi(portstr.str());
ptree->getProp("@service", bcfg->service_name);
ptree->getProp("@protocol", bcfg->protocol_name);
m_bindings.push_back(bcfg);
}
pt_iter->next();
}
pt_iter->Release();
pt_iter=NULL;
}
}
}
void CEspConfig::sendAlert(int severity, char const * descr, char const * subject) const
{
}
void CEspConfig::initDali(const char *servers)
{
if (servers!=NULL && *servers!=0 && !daliClientActive())
{
DBGLOG("Initializing DALI client [servers = %s]", servers);
useDali=true;
// Create server group
Owned serverGroup = createIGroup(servers, DALI_SERVER_PORT);
if (!serverGroup)
throw MakeStringException(0, "Could not instantiate dali IGroup");
// Initialize client process
if (!initClientProcess(serverGroup, DCR_EspServer))
throw MakeStringException(0, "Could not initialize dali client");
setPasswordsFromSDS();
serverstatus = new CSDSServerStatus("ESPserver");
}
}
void CEspConfig::initPtree(const char *location, bool isDali)
{
IPropertyTree* cfg = createPTreeFromXMLFile(location, ipt_caseInsensitive);
if (cfg)
{
cfg->addProp("@config", location);
m_envpt.setown(cfg);
}
}
void CEspConfig::loadBinding(binding_cfg &xcfg)
{
map::iterator sit = m_services.find(xcfg.service_name.str());
map::iterator pit = m_protocols.find(xcfg.protocol_name.str());
IEspService *isrv = NULL;
IEspProtocol *iprot = NULL;
if(sit == m_services.end())
{
DBGLOG("Warning: Service %s not found for the binding", xcfg.service_name.str());
}
else
{
isrv = (*sit).second->srv;
}
if(pit == m_protocols.end())
{
throw MakeStringException(-1, "Protocol %s not found for the binding", xcfg.protocol_name.str());
}
else
{
iprot = (*pit).second->prot;
if (iprot)
{
esp_binding_factory_t xproc = NULL;
if(isrv != NULL)
xcfg.service.setown(LINK(isrv));
xcfg.protocol.setown(LINK(iprot));
builtin *pdirect = getBuiltIn(xcfg.plugin.str());
if (pdirect)
{
xproc = pdirect->bind;
}
else
{
Owned pplg = getPlugin(xcfg.plugin.str());
if (pplg)
{
xproc = (esp_binding_factory_t) pplg->getProcAddress("esp_binding_factory");
}
}
if (xproc)
{
IEspRpcBinding* bind = xproc(xcfg.name.str(), xcfg.type.str(), m_envpt.get(), m_process.str());
if (bind)
DBGLOG("Load binding %s (type: %s, process: %s) succeeded", xcfg.name.str(), xcfg.type.str(), m_process.str());
else
ERRLOG("Failed to load binding %s (type: %s, process: %s)", xcfg.name.str(), xcfg.type.str(), m_process.str());
xcfg.bind.setown(bind);
if (serverstatus)
{
IPropertyTree *stTree= serverstatus->queryProperties()->addPropTree("ESPservice", createPTree("ESPservice", ipt_caseInsensitive));
if (stTree)
{
stTree->setProp("@type", xcfg.service->getServiceType());
stTree->setProp("@name", xcfg.service_name.str());
stTree->setPropInt("@port", xcfg.port);
}
serverstatus->commitProperties();
}
}
else
throw MakeStringException(-1, "procedure esp_binding_factory can't be loaded");
}
else
{
throw MakeStringException(-1, "Protocol %s wasn't loaded correctly for the binding", xcfg.protocol_name.str());
}
}
}
void CEspConfig::loadProtocol(protocol_cfg &xcfg)
{
esp_protocol_factory_t xproc = NULL;
builtin *pdirect = getBuiltIn(xcfg.plugin.str());
if (pdirect)
xproc = pdirect->prot;
else
{
Owned pplg = getPlugin(xcfg.plugin.str());
if (pplg)
{
xproc = (esp_protocol_factory_t) pplg->getProcAddress("esp_protocol_factory");
}
}
if (xproc)
{
xcfg.prot.setown(xproc(xcfg.name.str(), xcfg.type.str(), m_envpt.get(), m_process.str()));
if (xcfg.prot)
xcfg.prot->init(m_envpt.get(), m_process.str(), xcfg.name.str());
}
else
throw MakeStringException(-1, "procedure esp_protocol_factory can't be loaded");
}
void CEspConfig::loadService(srv_cfg &xcfg)
{
esp_service_factory_t xproc = NULL;
builtin *pdirect = getBuiltIn(xcfg.plugin.str());
if (pdirect)
xproc = pdirect->serv;
else
{
Owned pplg = getPlugin(xcfg.plugin.str());
if (pplg)
xproc = (esp_service_factory_t) pplg->getProcAddress("esp_service_factory");
}
if (xproc)
xcfg.srv.setown(xproc(xcfg.name.str(), xcfg.type.str(), m_envpt.get(), m_process.str()));
else
throw MakeStringException(-1, "procedure esp_service_factory can't be loaded");
}
void CEspConfig::loadServices()
{
map::iterator iter = m_services.begin();
while (iter!=m_services.end())
{
loadService(*(iter->second));
iter++;
}
}
void CEspConfig::loadProtocols()
{
map::iterator iter = m_protocols.begin();
while (iter!=m_protocols.end())
{
loadProtocol(*(iter->second));
iter++;
}
}
void CEspConfig::loadBindings()
{
list::iterator iter = m_bindings.begin();
while (iter!=m_bindings.end())
{
loadBinding(**iter);
iter++;
}
}
class ESPxsltIncludeHandler : public CInterface, implements IIncludeHandler
{
public:
// IMPLEMENT_IINTERFACE;
virtual void Link() const
{
CInterface::Link();
}
virtual bool Release() const
{
return CInterface::Release();
}
ESPxsltIncludeHandler()
{
}
~ESPxsltIncludeHandler()
{
}
inline bool fileExists(StringBuffer &filename)
{
return (checkFileExists(filename.str()) || checkFileExists(filename.toUpperCase().str()) || checkFileExists(filename.toLowerCase().str()));
}
inline bool fileRead(const char *filename, MemoryBuffer &buff)
{
Owned fi=createIFile(filename);
if (fi)
{
Owned fio=fi->open(IFOread);
if (fio)
{
offset_t len=fio->size();
if (fio->read(0, len, buff.reserveTruncate(len))==len)
return true;
}
}
buff.clear();
return false;
}
const char *pastLast(const char *src, const char *fnd)
{
int fndlen=(fnd) ? strlen(fnd) : 0;
int srclen=(src) ? strlen(src) : 0;
if (fndlen && srclen)
{
while (srclen--)
{
if (!strnicmp(src+srclen, fnd, fndlen))
return src+srclen+fndlen;
}
}
return NULL;
}
//IIncludeHandler
bool getInclude(const char* includename, MemoryBuffer& includebuf, bool& pathOnly)
{
if(!includename)
return false;
pathOnly = true;
includebuf.clear();
const char *finger=pastLast(includename, "esp/xslt/");
if (finger)
{
StringBuffer filepath;
if (fileExists(filepath.append(getCFD()).append("smc_xslt/").append(finger)) || fileExists(filepath.clear().append(getCFD()).append("xslt/").append(finger)))
{
includebuf.append(filepath.length(), filepath.str());
return true;
}
}
else
{
// First of all, it's better to use absolute path to specify the include, like /esp/xslt/ui_overrides.xslt.
// When you specify the include as relative path, for example ./ui_overrides.xslt
// the path will be expanded (by xmllib's source resolver) to its full path, beginning with file://
// on windows it looks like: file:///C:/playground/esp_lsb2/xslt/ui_overrides.xslt
// on linux: file:///home/yma/playground/esp_lsb2/xslt/ui_overrides.xslt
char dir[_MAX_PATH];
GetCurrentDirectory(sizeof(dir), dir);
#ifdef _WIN32
for(int i = 0; i < _MAX_PATH; i++)
{
if(dir[i] == '\0')
break;
else if(dir[i] == PATHSEPCHAR)
dir[i] = '/';
}
#endif
finger = strstr(includename, dir);
if(finger)
{
finger += strlen(dir) + 1;
StringBuffer filepath(finger);
if (fileExists(filepath))
{
includebuf.append(filepath.length(), filepath.str());
return true;
}
}
}
return false;
}
};
ESPxsltIncludeHandler g_includeHandler;
void CEspConfig::bindServer(IEspServer &server, IEspContainer &container)
{
list::iterator bit = m_bindings.begin();
while (bit != m_bindings.end())
{
binding_cfg *pbfg = *bit;
if (pbfg && pbfg->bind && pbfg->service && pbfg->protocol)
{
map::iterator pit = m_protocols.find(pbfg->protocol_name.str());
if(pit == m_protocols.end())
DBGLOG("Protocol %s not found for binding %s", pbfg->protocol_name.str(), pbfg->name.str());
else
{
Owned xslp=getXslProcessor();
if (xslp)
{
xslp->setDefIncludeHandler(dynamic_cast(&g_includeHandler));
pbfg->bind->setXslProcessor(xslp);
}
pbfg->bind->setContainer(&container);
pbfg->service->setContainer(&container);
pbfg->protocol->setContainer(&container);
pbfg->bind->addProtocol(pbfg->protocol->getProtocolName(), *pbfg->protocol.get());
if(pbfg->service != NULL)
pbfg->bind->addService(pbfg->service->getServiceType(), pbfg->address.str(), pbfg->port, *pbfg->service.get());
IEspProtocol* prot = (*pit).second->prot;
server.addBinding(pbfg->name.str(), pbfg->address.str(), pbfg->port, *prot, *(pbfg->bind.get()), pbfg->isDefault, m_cfg.get());
}
}
else
{
ERRLOG("Binding %s wasn't loaded correctly", pbfg->name.str());
}
bit++;
}
}
void CEspConfig::unloadBindings()
{
list::iterator iter = m_bindings.begin();
while (iter!=m_bindings.end())
{
binding_cfg *bcfg = *iter;
if(bcfg!=NULL)
{
bcfg->protocol.clear();
bcfg->bind.clear();
bcfg->service.clear();
delete bcfg;
}
iter++;
}
m_bindings.clear();
}
void CEspConfig::unloadServices()
{
map::iterator srvi = m_services.begin();
while (srvi!=m_services.end())
{
srv_cfg* scfg = srvi->second;
if(scfg)
{
scfg->cfg.clear();
scfg->srv.clear();
delete scfg;
}
srvi++;
}
m_services.clear();
}
void CEspConfig::unloadProtocols()
{
map::iterator proti = m_protocols.begin();
while (proti!=m_protocols.end())
{
protocol_cfg *pcfg = proti->second;
if(pcfg)
{
pcfg->prot.clear();
pcfg->cfg.clear();
delete pcfg;
}
proti++;
}
m_protocols.clear();
}
IEspPlugin* CEspConfig::getPlugin(const char* name)
{
if(!name || !*name)
return NULL;
ForEachItemIn(x, m_plugins)
{
IEspPlugin* plgn = &m_plugins.item(x);
if(plgn && stricmp(name, plgn->getName()) == 0)
{
return LINK(plgn);
}
}
Owned pplg = loadPlugin(name);
if(pplg)
{
pplg->Link(); //YMA: intentional leak. Unloading DLLs during ESP shutdown causes all kinds of issues.
m_plugins.append(*LINK(pplg));
return LINK(pplg);
}
return NULL;
}