mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
1842 lines
57 KiB
C++
1842 lines
57 KiB
C++
|
|
#include "stdafx.h"
|
|
|
|
#include <atlsync.h>
|
|
|
|
#include "gSOAP/generated/soapRCCServiceSoapService.h"
|
|
#include "rbx/ProcessPerfCounter.h"
|
|
#include "rbx/Debug.h"
|
|
#include "util/ProtectedString.h"
|
|
#include "util/StandardOut.h"
|
|
#include "boost/noncopyable.hpp"
|
|
#include "boost/thread/xtime.hpp"
|
|
#include "boost/property_tree/json_parser.hpp"
|
|
#include "boost/foreach.hpp"
|
|
|
|
#pragma comment(lib, "opengl32.lib")
|
|
#pragma comment(lib, "glu32.lib")
|
|
|
|
|
|
|
|
#include "v8datamodel/workspace.h"
|
|
#include "v8datamodel/contentprovider.h"
|
|
#include "v8datamodel/datamodel.h"
|
|
#include "v8datamodel/DebugSettings.h"
|
|
#include "v8datamodel/PhysicsSettings.h"
|
|
#include "v8datamodel/FastLogSettings.h"
|
|
#include "script/LuaSettings.h"
|
|
#include "v8datamodel/GameSettings.h"
|
|
#include "v8datamodel/MarketplaceService.h"
|
|
#include "v8datamodel/AdService.h"
|
|
#include "v8datamodel/CSGDictionaryService.h"
|
|
#include "v8datamodel/NonReplicatedCSGDictionaryService.h"
|
|
#include "v8datamodel/Message.h"
|
|
#include "RobloxServicesTools.h"
|
|
#include "script/scriptcontext.h"
|
|
#include "ThumbnailGenerator.h"
|
|
#include <string>
|
|
#include <sstream>
|
|
#include "V8DataModel/FactoryRegistration.h"
|
|
#include "ThumbnailGenerator.h"
|
|
#include "util/profiling.h"
|
|
#include "util/SoundService.h"
|
|
#include "Util/Guid.h"
|
|
#include "Util/Http.h"
|
|
#include "Util/Statistics.h"
|
|
#include "util/rbxrandom.h"
|
|
#include "network/api.h"
|
|
#include "VersionInfo.h"
|
|
#include "LogManager.h"
|
|
#include <queue>
|
|
#include "shlobj.h"
|
|
#include "DumpErrorUploader.h"
|
|
#include "gui/ProfanityFilter.h"
|
|
#include "GfxBase/ViewBase.h"
|
|
#include "util/FileSystem.h"
|
|
#include "Network/Players.h"
|
|
#include "v8xml/XmlSerializer.h"
|
|
#include "v8xml/WebParser.h"
|
|
#include "RobloxServicesTools.h"
|
|
#include "util/Utilities.h"
|
|
#include "Network/ChatFilter.h"
|
|
#include "Util/RobloxGoogleAnalytics.h"
|
|
#include "network/WebChatFilter.h"
|
|
|
|
#include "CountersClient.h"
|
|
|
|
#include "SimpleJSON.h"
|
|
#include "RbxFormat.h"
|
|
|
|
#include <tlhelp32.h>
|
|
#include <psapi.h>
|
|
#include "util/Analytics.h"
|
|
#include "OperationalSecurity.h"
|
|
|
|
|
|
#define BOOST_DATE_TIME_NO_LIB
|
|
#include "boost/date_time/posix_time/posix_time.hpp"
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <strsafe.h>
|
|
|
|
long diagCount=0;
|
|
long batchJobCount=0;
|
|
long openJobCount=0;
|
|
long closeJobCount=0;
|
|
long helloWorldCount=0;
|
|
long getVersionCount=0;
|
|
long renewLeaseCount=0;
|
|
long executeCount=0;
|
|
long getExpirationCount=0;
|
|
long getStatusCount=0;
|
|
long getAllJobsCount=0;
|
|
long closeExpiredJobsCount=0;
|
|
long closeAllJobsCount=0;
|
|
|
|
//#define DIAGNOSTICS
|
|
|
|
#ifdef DIAGNOSTICS
|
|
#define BEGIN_PRINT(func, msg) \
|
|
static int func = 0; \
|
|
int counter = func++; \
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Begin-%s%d\r\n", msg,counter)
|
|
#define END_PRINT(func, msg) \
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "End---%s%d\r\n", msg,counter)
|
|
#else
|
|
#define BEGIN_PRINT(func, msg)
|
|
#define END_PRINT(func, msg)
|
|
#endif
|
|
|
|
LOGVARIABLE(RCCServiceInit, 1);
|
|
LOGVARIABLE(RCCServiceJobs, 1);
|
|
LOGVARIABLE(RCCDataModelInit, 1);
|
|
// See TaskScheduler::ThreadPoolConfig enum for valid values
|
|
DYNAMIC_FASTINTVARIABLE(TaskSchedulerThreadCountEnum, 1)
|
|
|
|
DYNAMIC_FASTFLAGVARIABLE(DebugCrashOnFailToLoadClientSettings, false)
|
|
|
|
DYNAMIC_FASTFLAGVARIABLE(UseNewSecurityKeyApi, false);
|
|
DYNAMIC_FASTFLAGVARIABLE(UseNewMemHashApi, false);
|
|
DYNAMIC_FASTSTRINGVARIABLE(MemHashConfig, "");
|
|
FASTINTVARIABLE(RCCServiceThreadCount, RBX::TaskScheduler::Threads1)
|
|
DYNAMIC_FASTFLAGVARIABLE(US30476, false);
|
|
FASTFLAGVARIABLE(UseDataDomain, true);
|
|
|
|
FASTFLAGVARIABLE(Dep, true)
|
|
|
|
namespace RBX
|
|
{
|
|
class Explosion;
|
|
class Cofm;
|
|
class NormalBreakConnector;
|
|
class ContactConnector;
|
|
class RevoluteLink;
|
|
class SimBody;
|
|
class BallBallContact;
|
|
class BallBlockContact;
|
|
class BlockBlockContact;
|
|
|
|
}
|
|
|
|
namespace
|
|
{
|
|
static bool isServiceInstalled(const char* svcName);
|
|
}
|
|
|
|
class CrashAfterTimeout
|
|
{
|
|
shared_ptr<boost::condition_variable_any> event;
|
|
shared_ptr<boost::mutex> mutex;
|
|
|
|
static void run(shared_ptr<boost::condition_variable_any> event, int timeount, shared_ptr<boost::mutex> mutex)
|
|
{
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC_);
|
|
xt.sec += static_cast<boost::xtime::xtime_sec_t>(timeount);
|
|
boost::mutex::scoped_lock lock(*mutex);
|
|
bool set = event->timed_wait(lock, xt);
|
|
if (!set)
|
|
RBXCRASH();
|
|
}
|
|
public:
|
|
CrashAfterTimeout(int seconds):event(new boost::condition_variable_any()),mutex(new boost::mutex())
|
|
{
|
|
boost::thread thread(boost::bind(&CrashAfterTimeout::run, event, seconds, mutex));
|
|
}
|
|
~CrashAfterTimeout()
|
|
{
|
|
boost::mutex::scoped_lock lock(*mutex);
|
|
event->notify_all();
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
class RCCServiceSettings : public RBX::FastLogJSON
|
|
{
|
|
public:
|
|
START_DATA_MAP(RCCServiceSettings);
|
|
DECLARE_DATA_STRING(WindowsMD5);
|
|
DECLARE_DATA_STRING(WindowsPlayerBetaMD5);
|
|
DECLARE_DATA_STRING(MacMD5);
|
|
DECLARE_DATA_INT(SecurityDataTimer);
|
|
DECLARE_DATA_INT(ClientSettingsTimer);
|
|
DECLARE_DATA_STRING(GoogleAnalyticsAccountPropertyID);
|
|
DECLARE_DATA_INT(GoogleAnalyticsThreadPoolMaxScheduleSize);
|
|
DECLARE_DATA_INT(GoogleAnalyticsLoad);
|
|
DECLARE_DATA_BOOL(GoogleAnalyticsInitFix);
|
|
DECLARE_DATA_INT(HttpUseCurlPercentageRCC);
|
|
END_DATA_MAP();
|
|
};
|
|
|
|
class SecurityDataUpdater
|
|
{
|
|
std::string apiUrl;
|
|
|
|
protected:
|
|
std::string data;
|
|
|
|
virtual void processDataArray(shared_ptr<const RBX::Reflection::ValueArray> dataArray) = 0;
|
|
bool fetchData()
|
|
{
|
|
std::string newData;
|
|
try
|
|
{
|
|
RBX::Http request(apiUrl);
|
|
|
|
request.get(newData);
|
|
|
|
// no need to continue if data did not change
|
|
if (newData == data)
|
|
return false;
|
|
}
|
|
catch (std::exception& ex)
|
|
{
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_WARNING, "SecurityDataUpdater failed to fetch data from %s, %s", apiUrl.c_str(), ex.what());
|
|
return false;
|
|
}
|
|
|
|
data = newData;
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
SecurityDataUpdater(const std::string& url) : apiUrl(url) {}
|
|
virtual ~SecurityDataUpdater() {}
|
|
|
|
void run()
|
|
{
|
|
if (!fetchData())
|
|
return;
|
|
|
|
shared_ptr<const RBX::Reflection::ValueTable> jsonResult(rbx::make_shared<const RBX::Reflection::ValueTable>());
|
|
if(RBX::WebParser::parseJSONTable(data, jsonResult))
|
|
{
|
|
RBX::Reflection::ValueTable::const_iterator iter = jsonResult->find("data");
|
|
if (iter != jsonResult->end())
|
|
{
|
|
shared_ptr<const RBX::Reflection::ValueArray> dataArray = iter->second.cast<shared_ptr<const RBX::Reflection::ValueArray> >();
|
|
processDataArray(dataArray);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class MD5Updater : public SecurityDataUpdater
|
|
{
|
|
protected:
|
|
void processDataArray(shared_ptr<const RBX::Reflection::ValueArray> dataArray)
|
|
{
|
|
std::set<std::string> hashes;
|
|
for (RBX::Reflection::ValueArray::const_iterator it = dataArray->begin(); it != dataArray->end(); ++it)
|
|
{
|
|
std::string value = it->get<std::string>();
|
|
hashes.insert(value);
|
|
}
|
|
|
|
// always add hash for ios
|
|
hashes.insert("ios,ios");
|
|
|
|
RBX::Network::Players::setGoldenHashes2(hashes);
|
|
}
|
|
|
|
public:
|
|
MD5Updater(const std::string& url) : SecurityDataUpdater(url) {}
|
|
~MD5Updater() {}
|
|
};
|
|
|
|
class SecurityKeyUpdater : public SecurityDataUpdater
|
|
{
|
|
protected:
|
|
void processDataArray(shared_ptr<const RBX::Reflection::ValueArray> dataArray)
|
|
{
|
|
std::vector<std::string> versions;
|
|
for (RBX::Reflection::ValueArray::const_iterator it = dataArray->begin(); it != dataArray->end(); ++it)
|
|
{
|
|
// version = data + salt
|
|
std::string value = it->get<std::string>();
|
|
std::string version = RBX::sha1(value + "askljfLUZF");
|
|
versions.push_back(version);
|
|
}
|
|
|
|
RBX::Network::setSecurityVersions(versions);
|
|
}
|
|
|
|
public:
|
|
SecurityKeyUpdater(const std::string& url) : SecurityDataUpdater(url) {}
|
|
~SecurityKeyUpdater() {}
|
|
};
|
|
|
|
class MemHashUpdater : public SecurityDataUpdater
|
|
{
|
|
public:
|
|
// this is public until we get a web api to do this better.
|
|
static void populateMemHashConfigs(RBX::Network::MemHashConfigs& hashConfigs, const std::string& cfgStr)
|
|
{
|
|
hashConfigs.push_back(RBX::Network::MemHashVector());
|
|
RBX::Network::MemHashVector& thisHashVector = hashConfigs.back();
|
|
|
|
std::vector<std::string> hashStrInfo;
|
|
boost::split(hashStrInfo, cfgStr, boost::is_any_of(";"));
|
|
for (std::vector<std::string>::const_iterator hpIt = hashStrInfo.begin(); hpIt != hashStrInfo.end(); ++hpIt)
|
|
{
|
|
std::vector<std::string> args;
|
|
boost::split(args, *hpIt, boost::is_any_of(","));
|
|
if (args.size() >= 3)
|
|
{
|
|
RBX::Network::MemHash thisHash;
|
|
thisHash.checkIdx = boost::lexical_cast<unsigned int>(args[0]);
|
|
thisHash.value = boost::lexical_cast<unsigned int>(args[1]);
|
|
thisHash.failMask = boost::lexical_cast<unsigned int>(args[2]);
|
|
thisHashVector.push_back(thisHash);
|
|
}
|
|
}
|
|
};
|
|
protected:
|
|
void processDataArray(shared_ptr<const RBX::Reflection::ValueArray> dataArray)
|
|
{
|
|
RBX::Network::MemHashConfigs hashConfigs;
|
|
for (RBX::Reflection::ValueArray::const_iterator it = dataArray->begin(); it != dataArray->end(); ++it)
|
|
{
|
|
populateMemHashConfigs(hashConfigs, it->get<std::string>());
|
|
}
|
|
|
|
RBX::Network::Players::setGoldMemHashes(hashConfigs);
|
|
}
|
|
|
|
public:
|
|
MemHashUpdater(const std::string& url) : SecurityDataUpdater(url) {}
|
|
~MemHashUpdater() {}
|
|
};
|
|
|
|
|
|
class RCCServiceDynamicSettings : public RBX::FastLogJSON
|
|
{
|
|
typedef std::map<std::string, std::string> FVariables;
|
|
FVariables fVars;
|
|
|
|
public:
|
|
virtual void ProcessVariable(const std::string& valueName, const std::string& valueData, bool dynamic)
|
|
{
|
|
RBXASSERT(dynamic);
|
|
fVars.insert(std::make_pair(valueName, valueData));
|
|
}
|
|
|
|
virtual bool DefaultHandler(const std::string& valueName, const std::string& valueData)
|
|
{
|
|
// only process dynamic flags and logs
|
|
if (valueName[0] == 'D')
|
|
return FastLogJSON::DefaultHandler(valueName, valueData);
|
|
|
|
return false;
|
|
}
|
|
|
|
void UpdateSettings()
|
|
{
|
|
for (FVariables::iterator i = fVars.begin(); i != fVars.end(); i++)
|
|
FLog::SetValue(i->first, i->second, FASTVARTYPE_DYNAMIC, false);
|
|
fVars.clear();
|
|
}
|
|
};
|
|
|
|
|
|
DATA_MAP_IMPL_START(RCCServiceSettings)
|
|
IMPL_DATA(WindowsMD5, "");
|
|
IMPL_DATA(MacMD5, "");
|
|
IMPL_DATA(WindowsPlayerBetaMD5, "");
|
|
IMPL_DATA(SecurityDataTimer, 300); // in seconds, default 5 min
|
|
IMPL_DATA(ClientSettingsTimer, 120); // 2 min
|
|
IMPL_DATA(GoogleAnalyticsAccountPropertyID, "UA-43420590-13"); // Google Analytics Game Server Analytics Test account
|
|
IMPL_DATA(GoogleAnalyticsThreadPoolMaxScheduleSize, 500); // max items to be scheduled at any one time by the G.A. threadpool
|
|
IMPL_DATA(GoogleAnalyticsLoad, 10); // percent probability of using google analytics
|
|
IMPL_DATA(GoogleAnalyticsInitFix, true);
|
|
IMPL_DATA(HttpUseCurlPercentageRCC, 0); // do not use CURL by default
|
|
DATA_MAP_IMPL_END()
|
|
|
|
static boost::scoped_ptr<MainLogManager> mainLogManager(new MainLogManager("Roblox Web Service", ".dmp", ".crashevent"));
|
|
|
|
#ifdef RBX_TEST_BUILD
|
|
std::string RCCServiceSettingsKeyOverwrite;
|
|
#endif
|
|
|
|
class CWebService
|
|
{
|
|
boost::shared_ptr<CProcessPerfCounter> s_perfCounter;
|
|
boost::shared_ptr<RBX::ProfanityFilter> s_profanityFilter;
|
|
boost::scoped_ptr<boost::thread> perfData;
|
|
boost::scoped_ptr<boost::thread> fetchSecurityDataThread;
|
|
boost::scoped_ptr<boost::thread> fetchClientSettingsThread;
|
|
ATL::CEvent doneEvent;
|
|
RCCServiceSettings rccSettings;
|
|
RCCServiceDynamicSettings rccDynamicSettings;
|
|
std::string settingsKey;
|
|
std::string securityVersionData;
|
|
std::string clientSettingsData;
|
|
|
|
std::string thumbnailSettingsData;
|
|
bool isThumbnailer;
|
|
|
|
boost::scoped_ptr<MD5Updater> md5Updater;
|
|
boost::scoped_ptr<SecurityDataUpdater> securityKeyUpdater;
|
|
boost::scoped_ptr<MemHashUpdater> memHashUpdater;
|
|
|
|
boost::scoped_ptr<CountersClient> counters;
|
|
|
|
public:
|
|
struct JobItem : boost::noncopyable
|
|
{
|
|
enum JobItemRunStatus { RUNNING_JOB, JOB_DONE, JOB_ERROR };
|
|
const std::string id;
|
|
shared_ptr<RBX::DataModel> dataModel;
|
|
RBX::Time expirationTime;
|
|
int category;
|
|
double cores;
|
|
ATL::CEvent jobCheckLeaseEvent;
|
|
rbx::signals::connection notifyAliveConnection;
|
|
|
|
JobItemRunStatus status;
|
|
std::string errorMessage;
|
|
|
|
JobItem(const char* id):id(id),jobCheckLeaseEvent(TRUE, FALSE), status(RUNNING_JOB) {}
|
|
|
|
void touch(double seconds);
|
|
double secondsToTimeout() const;
|
|
};
|
|
|
|
typedef std::map<std::string, boost::shared_ptr<JobItem> > JobMap;
|
|
JobMap jobs;
|
|
boost::mutex sync;
|
|
long dataModelCount;
|
|
boost::mutex currentlyClosingMutex;
|
|
public:
|
|
CWebService(bool crashUploadOnly);
|
|
~CWebService();
|
|
private:
|
|
void CWebService::collectPerfData();
|
|
void LoadAppSettings();
|
|
void LoadClientSettings(RCCServiceSettings& dest);
|
|
void LoadClientSettings(std::string& clientDest, std::string& thumbnailDest);
|
|
std::string GetSettingsKey();
|
|
|
|
std::vector<std::string> fetchAllowedSecurityVersions();
|
|
public:
|
|
static boost::scoped_ptr<CWebService> singleton;
|
|
JobMap::iterator getJob(const std::string& jobID)
|
|
{
|
|
JobMap::iterator iter = jobs.find(jobID);
|
|
if (iter==jobs.end())
|
|
throw RBX::runtime_error("JobItem %s not found", jobID.c_str());
|
|
return iter;
|
|
}
|
|
|
|
void validateSecurityData()
|
|
{
|
|
while (::WaitForSingleObject(doneEvent.m_h, rccSettings.GetValueSecurityDataTimer() * 1000) == WAIT_TIMEOUT)
|
|
{
|
|
if (DFFlag::UseNewSecurityKeyApi)
|
|
{
|
|
if (securityKeyUpdater)
|
|
securityKeyUpdater->run();
|
|
}
|
|
else
|
|
{
|
|
std::string prevVersionData = securityVersionData;
|
|
std::vector<std::string> versions = fetchAllowedSecurityVersions();
|
|
|
|
// security versions changed, update all jobs with new versions
|
|
if (prevVersionData != securityVersionData)
|
|
RBX::Network::setSecurityVersions(versions);
|
|
}
|
|
|
|
if (DFFlag::UseNewMemHashApi)
|
|
{
|
|
if (memHashUpdater)
|
|
{
|
|
if (DFString::MemHashConfig.size() < 3)
|
|
{
|
|
memHashUpdater->run();
|
|
}
|
|
else
|
|
{
|
|
RBX::Network::MemHashConfigs hashConfigs;
|
|
MemHashUpdater::populateMemHashConfigs(hashConfigs, DFString::MemHashConfig);
|
|
RBX::Network::Players::setGoldMemHashes(hashConfigs);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (md5Updater)
|
|
md5Updater->run();
|
|
|
|
}
|
|
}
|
|
|
|
void validateClientSettings()
|
|
{
|
|
while (::WaitForSingleObject(doneEvent.m_h, rccSettings.GetValueClientSettingsTimer() * 1000) == WAIT_TIMEOUT)
|
|
{
|
|
std::string prevClientSettings = clientSettingsData;
|
|
std::string prevThumbnailSettings = thumbnailSettingsData;
|
|
|
|
LoadClientSettings(clientSettingsData, thumbnailSettingsData);
|
|
|
|
bool clientChanged = (prevClientSettings != clientSettingsData);
|
|
bool thumbnailChanged = isThumbnailer && (prevThumbnailSettings != thumbnailSettingsData);
|
|
if (clientChanged || thumbnailChanged)
|
|
{
|
|
// collect all dynamic settings
|
|
rccDynamicSettings.ReadFromStream(clientSettingsData.c_str());
|
|
if (isThumbnailer)
|
|
{
|
|
rccDynamicSettings.ReadFromStream(thumbnailSettingsData.c_str());
|
|
}
|
|
|
|
// submit datamodel write task to update all dynamic settings
|
|
boost::mutex::scoped_lock lock(sync);
|
|
if (jobs.size() > 0)
|
|
{
|
|
// HACK: Client settings are global, meaning all datamodels use the same set,
|
|
// current we only have 1 datamodel running per rccservice, so submit write task just on first datamodel
|
|
shared_ptr<JobItem> job = jobs.begin()->second;
|
|
job->dataModel->submitTask(boost::bind(&RCCServiceDynamicSettings::UpdateSettings, &rccDynamicSettings), RBX::DataModelJob::Write);
|
|
}
|
|
else
|
|
rccDynamicSettings.UpdateSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
void doCheckLease(boost::shared_ptr<JobItem> job)
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
double sleepTimeInSeconds = job->secondsToTimeout();
|
|
if (sleepTimeInSeconds<=0)
|
|
{
|
|
closeJob(job->id);
|
|
return;
|
|
}
|
|
else if (::WaitForSingleObject(job->jobCheckLeaseEvent.m_h, (DWORD)(sleepTimeInSeconds * 1000.0) + 3)!=WAIT_TIMEOUT)
|
|
return;
|
|
}
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
assert(false);
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "doCheckLease failure: %s", e.what());
|
|
}
|
|
}
|
|
|
|
void renewLease(const std::string& id, double expirationInSeconds)
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
getJob(id)->second->touch(expirationInSeconds);
|
|
}
|
|
|
|
static void convertToLua(const RBX::Reflection::Variant& source, ns1__LuaValue& dest, soap* soap)
|
|
{
|
|
assert(dest.type == ns1__LuaType__LUA_USCORETNIL);
|
|
assert(dest.value == NULL);
|
|
|
|
if (source.isType<void>())
|
|
{
|
|
dest.type = ns1__LuaType__LUA_USCORETNIL;
|
|
return;
|
|
}
|
|
|
|
if (source.isType<bool>())
|
|
{
|
|
dest.type = ns1__LuaType__LUA_USCORETBOOLEAN;
|
|
dest.value = soap_new_std__string(soap, -1);
|
|
*dest.value = source.get<std::string>();
|
|
return;
|
|
}
|
|
|
|
if (source.isNumber())
|
|
{
|
|
dest.type = ns1__LuaType__LUA_USCORETNUMBER;
|
|
dest.value = soap_new_std__string(soap, -1);
|
|
*dest.value = source.get<std::string>();
|
|
return;
|
|
}
|
|
|
|
if (source.isType<std::string>())
|
|
{
|
|
dest.type = ns1__LuaType__LUA_USCORETSTRING;
|
|
dest.value = soap_new_std__string(soap, -1);
|
|
*dest.value = source.get<std::string>();
|
|
return;
|
|
}
|
|
|
|
if (source.isType<RBX::ContentId>())
|
|
{
|
|
dest.type = ns1__LuaType__LUA_USCORETSTRING;
|
|
dest.value = soap_new_std__string(soap, -1);
|
|
*dest.value = source.get<std::string>();
|
|
return;
|
|
}
|
|
|
|
if (source.isType<shared_ptr<const RBX::Reflection::ValueArray> >())
|
|
{
|
|
shared_ptr<const RBX::Reflection::ValueArray> collection = source.cast<shared_ptr<const RBX::Reflection::ValueArray> >();
|
|
dest.type = ns1__LuaType__LUA_USCORETTABLE;
|
|
dest.table = soap_new_ns1__ArrayOfLuaValue(soap, -1);
|
|
dest.table->LuaValue.resize(collection->size());
|
|
for (size_t i = 0; i<collection->size(); ++i)
|
|
{
|
|
dest.table->LuaValue[i] = soap_new_ns1__LuaValue(soap, -1);
|
|
convertToLua((*collection)[i], *dest.table->LuaValue[i], soap);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// TODO: enums!
|
|
dest.type = ns1__LuaType__LUA_USCORETNIL;
|
|
}
|
|
|
|
static void convert(const ns1__LuaValue* source, RBX::Reflection::Variant& dest)
|
|
{
|
|
switch (source->type)
|
|
{
|
|
case ns1__LuaType__LUA_USCORETNIL:
|
|
break;
|
|
|
|
case ns1__LuaType__LUA_USCORETNUMBER:
|
|
dest = *source->value;
|
|
dest.convert<double>();
|
|
break;
|
|
|
|
case ns1__LuaType__LUA_USCORETBOOLEAN:
|
|
dest = *source->value;
|
|
dest.convert<bool>();
|
|
break;
|
|
|
|
case ns1__LuaType__LUA_USCORETSTRING:
|
|
dest = *source->value;
|
|
break;
|
|
|
|
case ns1__LuaType__LUA_USCORETTABLE:
|
|
{
|
|
const size_t count = source->table->LuaValue.size();
|
|
|
|
// Create a collection so that we can populate it
|
|
shared_ptr<RBX::Reflection::ValueArray> table(new RBX::Reflection::ValueArray(count));
|
|
|
|
for (size_t i=0; i<count; ++i)
|
|
convert(source->table->LuaValue[i], (*table)[i]);
|
|
|
|
// Set the value to a ValueArray type
|
|
dest = shared_ptr<const RBX::Reflection::ValueArray>(table);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Gathers all non-global Instances that are in the heap
|
|
void arbiterActivityDump(RBX::Reflection::ValueArray& result)
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
for (JobMap::iterator iter = jobs.begin(); iter != jobs.end(); ++iter)
|
|
{
|
|
shared_ptr<RBX::DataModel> dataModel = iter->second->dataModel;
|
|
shared_ptr<RBX::Reflection::ValueArray> tuple(new RBX::Reflection::ValueArray());
|
|
tuple->push_back(dataModel->arbiterName());
|
|
tuple->push_back(dataModel->getAverageActivity());
|
|
result.push_back(shared_ptr<const RBX::Reflection::ValueArray>(tuple));
|
|
}
|
|
}
|
|
|
|
// Gathers all non-global Instances that are in the heap
|
|
void leakDump(RBX::Reflection::ValueArray& result)
|
|
{
|
|
}
|
|
|
|
// Gathers diagnostic data. Does NOT require locks on datamodel :)
|
|
const RBX::Reflection::ValueArray* diag(int type, shared_ptr<RBX::DataModel> dataModel)
|
|
{
|
|
std::auto_ptr<RBX::Reflection::ValueArray> tuple(new RBX::Reflection::ValueArray());
|
|
|
|
/* This is the format of the Diag data:
|
|
|
|
type == 0
|
|
DataModel Count in this process
|
|
PerfCounter data
|
|
Task Scheduler
|
|
(obsolete entry)
|
|
double threadAffinity
|
|
double numQueuedJobs
|
|
double numScheduledJobs
|
|
double numRunningJobs
|
|
long threadPoolSize
|
|
double messageRate
|
|
double messagePumpDutyCycle
|
|
DataModel Jobs Info
|
|
Machine configuration
|
|
Memory Leak Detection
|
|
type & 1
|
|
leak dump
|
|
type & 2
|
|
attempt to allocate 500k. if success, then true else false
|
|
type & 4
|
|
DataModel dutyCycles
|
|
*/
|
|
tuple->push_back(dataModelCount);
|
|
|
|
{
|
|
shared_ptr<RBX::Reflection::ValueArray> perfCounterData(new RBX::Reflection::ValueArray());
|
|
perfCounterData->push_back(s_perfCounter->GetProcessCores());
|
|
perfCounterData->push_back(s_perfCounter->GetTotalProcessorTime());
|
|
perfCounterData->push_back(s_perfCounter->GetProcessorTime());
|
|
perfCounterData->push_back(s_perfCounter->GetPrivateBytes());
|
|
perfCounterData->push_back(s_perfCounter->GetPrivateWorkingSetBytes());
|
|
perfCounterData->push_back(-1);
|
|
perfCounterData->push_back(-1);
|
|
perfCounterData->push_back(s_perfCounter->GetElapsedTime());
|
|
perfCounterData->push_back(s_perfCounter->GetVirtualBytes());
|
|
perfCounterData->push_back(s_perfCounter->GetPageFileBytes());
|
|
perfCounterData->push_back(s_perfCounter->GetPageFaultsPerSecond());
|
|
|
|
tuple->push_back(shared_ptr<const RBX::Reflection::ValueArray>(perfCounterData));
|
|
}
|
|
{
|
|
shared_ptr<RBX::Reflection::ValueArray> taskSchedulerData(new RBX::Reflection::ValueArray());
|
|
taskSchedulerData->push_back(0); // obsolete
|
|
taskSchedulerData->push_back(RBX::TaskScheduler::singleton().threadAffinity());
|
|
taskSchedulerData->push_back(RBX::TaskScheduler::singleton().numSleepingJobs());
|
|
taskSchedulerData->push_back(RBX::TaskScheduler::singleton().numWaitingJobs());
|
|
taskSchedulerData->push_back(RBX::TaskScheduler::singleton().numRunningJobs());
|
|
taskSchedulerData->push_back((long) RBX::TaskScheduler::singleton().threadPoolSize());
|
|
taskSchedulerData->push_back(RBX::TaskScheduler::singleton().schedulerRate());
|
|
taskSchedulerData->push_back(RBX::TaskScheduler::singleton().getSchedulerDutyCyclePerThread());
|
|
tuple->push_back(shared_ptr<const RBX::Reflection::ValueArray>(taskSchedulerData));
|
|
}
|
|
|
|
if (dataModel)
|
|
tuple->push_back(dataModel->getJobsInfo());
|
|
else
|
|
tuple->push_back(0);
|
|
|
|
{
|
|
shared_ptr<RBX::Reflection::ValueArray> machineData(new RBX::Reflection::ValueArray());
|
|
machineData->push_back(RBX::DebugSettings::singleton().totalPhysicalMemory());
|
|
machineData->push_back(RBX::DebugSettings::singleton().cpu());
|
|
machineData->push_back(RBX::DebugSettings::singleton().cpuCount());
|
|
machineData->push_back(RBX::DebugSettings::singleton().cpuSpeed());
|
|
tuple->push_back(shared_ptr<const RBX::Reflection::ValueArray>(machineData));
|
|
}
|
|
|
|
{
|
|
// TODO, use RBX::poolAllocationList and RBX::poolAvailablityList to get complete stats
|
|
shared_ptr<RBX::Reflection::ValueArray> memCounters(new RBX::Reflection::ValueArray());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::Instance>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::PartInstance>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::ModelInstance>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::Explosion>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::Soundscape::SoundChannel>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<rbx::signals::connection>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<rbx::signals::connection::islot>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::Reflection::GenericSlotWrapper>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::TaskScheduler::Job>::getCount());
|
|
memCounters->push_back(RBX::Diagnostics::Countable<RBX::Network::Player>::getCount());
|
|
#ifdef RBX_ALLOCATOR_COUNTS
|
|
memCounters->push_back(RBX::Allocator<RBX::Body>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::Cofm>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::NormalBreakConnector>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::ContactConnector>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::RevoluteLink>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::SimBody>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::BallBallContact>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::BallBlockContact>::getCount());
|
|
memCounters->push_back(RBX::Allocator<RBX::BlockBlockContact>::getCount());
|
|
memCounters->push_back(RBX::Allocator<XmlAttribute>::getCount());
|
|
memCounters->push_back(RBX::Allocator<XmlElement>::getCount());
|
|
#else
|
|
for(int i = 0; i<11; ++i)
|
|
memCounters->push_back(-1);
|
|
#endif
|
|
memCounters->push_back((int)ThumbnailGenerator::totalCount);
|
|
tuple->push_back(shared_ptr<const RBX::Reflection::ValueArray>(memCounters));
|
|
}
|
|
|
|
if (type & 1)
|
|
{
|
|
shared_ptr<RBX::Reflection::ValueArray> result(new RBX::Reflection::ValueArray());
|
|
leakDump(*result);
|
|
tuple->push_back(shared_ptr<const RBX::Reflection::ValueArray>(result));
|
|
}
|
|
|
|
if (type & 2)
|
|
{
|
|
try
|
|
{
|
|
{
|
|
std::vector<char> v1(100000);
|
|
std::vector<char> v2(100000);
|
|
std::vector<char> v3(100000);
|
|
std::vector<char> v4(100000);
|
|
std::vector<char> v5(100000);
|
|
}
|
|
tuple->push_back(true);
|
|
}
|
|
catch (...)
|
|
{
|
|
tuple->push_back(false);
|
|
}
|
|
}
|
|
|
|
if (type & 4)
|
|
{
|
|
shared_ptr<RBX::Reflection::ValueArray> result(new RBX::Reflection::ValueArray());
|
|
arbiterActivityDump(*result);
|
|
tuple->push_back(shared_ptr<const RBX::Reflection::ValueArray>(result));
|
|
}
|
|
|
|
return tuple.release();
|
|
}
|
|
|
|
void execute(const std::string& jobID, ns1__ScriptExecution* script, std::vector<ns1__LuaValue*> * result, soap* soap)
|
|
{
|
|
std::string code = *script->script;
|
|
if (RBX::ContentProvider::isHttpUrl(code))
|
|
{
|
|
RBX::Http http(code);
|
|
http.get(code);
|
|
}
|
|
|
|
shared_ptr<RBX::DataModel> dataModel;
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
JobMap::iterator iter = getJob(jobID);
|
|
dataModel = iter->second->dataModel;
|
|
}
|
|
|
|
std::auto_ptr<const RBX::Reflection::Tuple> tuple;
|
|
{
|
|
const size_t count = script->arguments ? script->arguments->LuaValue.size() : 0;
|
|
RBX::Reflection::Tuple args(count);
|
|
for (size_t i=0; i<count; ++i)
|
|
convert(script->arguments->LuaValue[i], args.values[i]);
|
|
|
|
RBX::DataModel::LegacyLock lock(dataModel, RBX::DataModelJob::Write);
|
|
if (dataModel->isClosed())
|
|
throw std::runtime_error("The DataModel is closed");
|
|
RBX::ScriptContext* scriptContext = RBX::ServiceProvider::create<RBX::ScriptContext>(dataModel.get());
|
|
tuple = scriptContext->executeInNewThread(RBX::Security::WebService, RBX::ProtectedString::fromTrustedSource(code), script->name->c_str(), args);
|
|
}
|
|
|
|
result->resize(tuple->values.size());
|
|
for (size_t i = 0; i<tuple->values.size(); ++i)
|
|
{
|
|
(*result)[i] = soap_new_ns1__LuaValue(soap, -1);
|
|
convertToLua(tuple->values[i], *(*result)[i], soap);
|
|
}
|
|
}
|
|
|
|
void diag(int type, std::string jobID, std::vector<ns1__LuaValue*>* result, soap* soap)
|
|
{
|
|
std::auto_ptr<const RBX::Reflection::ValueArray> tuple;
|
|
|
|
{
|
|
shared_ptr<RBX::DataModel> dataModel;
|
|
if (!jobID.empty())
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
JobMap::iterator iter = getJob(jobID);
|
|
dataModel = iter->second->dataModel;
|
|
}
|
|
tuple.reset(diag(type, dataModel));
|
|
}
|
|
|
|
result->resize(tuple->size());
|
|
for (size_t i = 0; i<tuple->size(); ++i)
|
|
{
|
|
(*result)[i] = soap_new_ns1__LuaValue(soap, -1);
|
|
convertToLua((*tuple)[i], *(*result)[i], soap);
|
|
}
|
|
}
|
|
|
|
void contentDataLoaded(shared_ptr<RBX::DataModel>& dataModel)
|
|
{
|
|
RBX::CSGDictionaryService* dictionaryService = RBX::ServiceProvider::create<RBX::CSGDictionaryService>(dataModel.get());
|
|
RBX::NonReplicatedCSGDictionaryService* nrDictionaryService = RBX::ServiceProvider::create<RBX::NonReplicatedCSGDictionaryService>(dataModel.get());
|
|
|
|
dictionaryService->reparentAllChildData();
|
|
}
|
|
|
|
void setupServerConnections(RBX::DataModel* dataModel)
|
|
{
|
|
if (!dataModel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (RBX::AdService* adService = dataModel->create<RBX::AdService>())
|
|
{
|
|
adService->sendServerVideoAdVerification.connect(boost::bind(&RBX::AdService::checkCanPlayVideoAd,adService,_1, _2));
|
|
adService->sendServerRecordImpression.connect(boost::bind(&RBX::AdService::sendAdImpression,adService,_1,_2,_3));
|
|
}
|
|
}
|
|
|
|
shared_ptr<JobItem> createJob(const ns1__Job& job, bool startHeartbeat, shared_ptr<RBX::DataModel>& dataModel)
|
|
{
|
|
srand(RBX::randomSeed()); // make sure this thread is seeded
|
|
std::string id = job.id;
|
|
|
|
dataModel = RBX::DataModel::createDataModel(startHeartbeat, new RBX::NullVerb(NULL,""), false);
|
|
setupServerConnections(dataModel.get());
|
|
|
|
RBX::Network::Players* players = dataModel->find<RBX::Network::Players>();
|
|
if(players)
|
|
{
|
|
LoadClientSettings(rccSettings);
|
|
if (rccSettings.GetError())
|
|
{
|
|
RBXCRASH(rccSettings.GetErrorString().c_str());
|
|
}
|
|
|
|
{
|
|
bool useCurl = rand() % 100 < rccSettings.GetValueHttpUseCurlPercentageRCC();
|
|
FASTLOG1(FLog::RCCDataModelInit, "Using CURL = %d", useCurl);
|
|
RBX::Http::SetUseCurl(useCurl);
|
|
|
|
RBX::Http::SetUseStatistics(true);
|
|
}
|
|
|
|
FASTLOGS(FLog::RCCDataModelInit, "Creating Data Model, Windows MD5: %s", rccSettings.GetValueWindowsMD5());
|
|
FASTLOGS(FLog::RCCDataModelInit, "Creating Data Model, Mac MD5: %s", rccSettings.GetValueMacMD5());
|
|
FASTLOGS(FLog::RCCDataModelInit, "Creating Data Model, Windows Player Beta MD5: %s", rccSettings.GetValueWindowsPlayerBetaMD5());
|
|
players->setGoldenHashes(rccSettings.GetValueWindowsMD5(), rccSettings.GetValueMacMD5(), rccSettings.GetValueWindowsPlayerBetaMD5());
|
|
|
|
if (rccSettings.GetValueGoogleAnalyticsInitFix())
|
|
{
|
|
RBX::Analytics::GoogleAnalytics::lotteryInit(rccSettings.GetValueGoogleAnalyticsAccountPropertyID(), rccSettings.GetValueGoogleAnalyticsLoad());
|
|
}
|
|
else
|
|
{
|
|
int lottery = rand() % 100;
|
|
FASTLOG1(FLog::RCCDataModelInit, "Google analytics lottery number = %d", lottery);
|
|
if (lottery < rccSettings.GetValueGoogleAnalyticsLoad())
|
|
{
|
|
FASTLOG1(FLog::RCCDataModelInit, "Setting Google Analytics ThreadPool Max Schedule Size: %d", rccSettings.GetValueGoogleAnalyticsThreadPoolMaxScheduleSize());
|
|
FASTLOGS(FLog::RCCDataModelInit, "Setting Google Analytics Account Property ID: %s", rccSettings.GetValueGoogleAnalyticsAccountPropertyID());
|
|
RBX::RobloxGoogleAnalytics::setCanUseAnalytics();
|
|
RBX::RobloxGoogleAnalytics::init(rccSettings.GetValueGoogleAnalyticsAccountPropertyID(), rccSettings.GetValueGoogleAnalyticsThreadPoolMaxScheduleSize());
|
|
}
|
|
}
|
|
|
|
RBX::DataModel::LegacyLock lock(dataModel, RBX::DataModelJob::Write);
|
|
dataModel->create<RBX::Network::WebChatFilter>();
|
|
}
|
|
|
|
dataModel->workspaceLoadedSignal.connect(boost::bind(&CWebService::contentDataLoaded, this, dataModel));
|
|
|
|
dataModel->jobId = id;
|
|
shared_ptr<JobItem> j(new JobItem(id.c_str()));
|
|
j->dataModel = dataModel;
|
|
j->category = job.category;
|
|
j->cores = job.cores;
|
|
j->touch(job.expirationInSeconds);
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
if (jobs.find(id)!=jobs.end())
|
|
throw RBX::runtime_error("JobItem %s already exists", id.c_str());
|
|
jobs[id] = j;
|
|
}
|
|
|
|
return j;
|
|
}
|
|
|
|
void batchJob(const ns1__Job& job, ns1__ScriptExecution* script, std::vector<ns1__LuaValue*>* result, soap* soap)
|
|
{
|
|
std::string id = job.id;
|
|
|
|
shared_ptr<RBX::DataModel> dataModel;
|
|
::InterlockedIncrement(&dataModelCount);
|
|
try
|
|
{
|
|
shared_ptr<JobItem> j = createJob(job, false, dataModel);
|
|
|
|
FASTLOGS(FLog::RCCServiceJobs, "Opened Batch JobItem %s", id.c_str());
|
|
FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", dataModel.get());
|
|
|
|
//Spin off a thread for the BatchJob to execute in
|
|
boost::thread(RBX::thread_wrapper(boost::bind(&CWebService::asyncExecute, this, id, script, result, soap), "AsyncExecute"));
|
|
|
|
while (true)
|
|
{
|
|
double sleepTimeInSeconds = j->secondsToTimeout();
|
|
|
|
if (sleepTimeInSeconds <= 0) // This case seems like an edge case (we already ran out of time)
|
|
{
|
|
if (::WaitForSingleObject(j->jobCheckLeaseEvent.m_h, 1)!=WAIT_TIMEOUT){
|
|
if(j->status == JobItem::JOB_ERROR)
|
|
throw RBX::runtime_error(j->errorMessage.c_str());
|
|
}
|
|
|
|
closeJob(id);
|
|
throw RBX::runtime_error("BatchJob Timeout");
|
|
}
|
|
else if (::WaitForSingleObject(j->jobCheckLeaseEvent.m_h, (DWORD)(sleepTimeInSeconds * 1000.0) + 3) == WAIT_TIMEOUT)
|
|
{
|
|
// do nothing, just continue looping. Probably sleepTimeInSeconds will be negative, and we'll throw
|
|
}
|
|
else
|
|
{
|
|
// jobCheckLeaseEvent was set
|
|
if(j->status == JobItem::JOB_ERROR)
|
|
throw RBX::runtime_error(j->errorMessage.c_str());
|
|
|
|
// TODO: Shouldn't this be called BEFORE we throw??????
|
|
closeJob(id);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception&)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void asyncExecute(const std::string& id, ns1__ScriptExecution* script, std::vector<ns1__LuaValue*> * result, soap* soap)
|
|
{
|
|
try
|
|
{
|
|
this->execute(id, script, result, soap);
|
|
closeJob(id);
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
char szMsg[1024];
|
|
StringCchCopy(szMsg, ARRAYSIZE(szMsg), e.what());
|
|
|
|
closeJob(id, szMsg);
|
|
}
|
|
}
|
|
void openJob(const ns1__Job& job, ns1__ScriptExecution* script, std::vector<ns1__LuaValue*>* result, soap* soap, bool startHeartbeat)
|
|
{
|
|
std::string id = job.id;
|
|
RBX::Http::gameID = job.id;
|
|
|
|
shared_ptr<RBX::DataModel> dataModel;
|
|
::InterlockedIncrement(&dataModelCount);
|
|
try
|
|
{
|
|
shared_ptr<JobItem> j = createJob(job, startHeartbeat, dataModel);
|
|
|
|
try
|
|
{
|
|
RBX::DataModel *pDataModel = dataModel.get();
|
|
|
|
// Monitor the job and close it if needed
|
|
boost::thread(RBX::thread_wrapper(boost::bind(&CWebService::doCheckLease, this, j), "Check Expiration"));
|
|
|
|
FASTLOGS(FLog::RCCServiceJobs, "Opened JobItem %s", id.c_str());
|
|
FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", pDataModel);
|
|
|
|
this->execute(id, script, result, soap);
|
|
|
|
RBX::RunService* runService = RBX::ServiceProvider::find<RBX::RunService>(pDataModel);
|
|
RBXASSERT(runService != NULL);
|
|
j->notifyAliveConnection = runService->heartbeatSignal.connect(boost::bind(&CWebService::notifyAlive, this, _1));
|
|
|
|
{
|
|
RBX::DataModel::LegacyLock lock(pDataModel, RBX::DataModelJob::Write);
|
|
pDataModel->loadCoreScripts();
|
|
}
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
char szMsg[1024];
|
|
StringCchCopy(szMsg, ARRAYSIZE(szMsg), e.what());
|
|
|
|
boost::mutex::scoped_lock lock(sync);
|
|
jobs.erase(id);
|
|
throw;
|
|
}
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
char szMsg[1024];
|
|
StringCchCopy(szMsg, ARRAYSIZE(szMsg), e.what());
|
|
|
|
FASTLOGS(FLog::RCCServiceJobs, "Closing DataModel due to exception: %s", szMsg);
|
|
FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", dataModel.get());
|
|
|
|
closeDataModel(dataModel);
|
|
dataModel.reset();
|
|
::InterlockedDecrement(&dataModelCount);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void closeDataModel(shared_ptr<RBX::DataModel> dataModel)
|
|
{
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
//CrashAfterTimeout crash(90); // If after 90 seconds the datamodel doesn't close, then CRASH!!!!
|
|
RBX::DataModel::closeDataModel(dataModel);
|
|
}
|
|
|
|
void notifyAlive(const RBX::Heartbeat& h)
|
|
{
|
|
mainLogManager->NotifyFGThreadAlive();
|
|
}
|
|
|
|
void closeJob(const std::string& jobID, const char* errorMessage = NULL)
|
|
{
|
|
// Take a mutex for the duration of closeJob here. The arbiter thinks that as
|
|
// soon as closeJob returns it is safe to force kill the rcc process, so make
|
|
// sure that if it is in the process of closing the datamodel any parallel requests
|
|
// to close the data model block at the mutex until the first one finishes.
|
|
boost::mutex::scoped_lock closeLock(currentlyClosingMutex);
|
|
|
|
shared_ptr<RBX::DataModel> dataModel;
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
JobMap::iterator iter = jobs.find(jobID);
|
|
if (iter != jobs.end())
|
|
{
|
|
if(errorMessage){
|
|
iter->second->errorMessage = errorMessage;
|
|
iter->second->status = JobItem::JOB_ERROR;
|
|
}
|
|
else{
|
|
iter->second->status = JobItem::JOB_DONE;
|
|
}
|
|
iter->second->jobCheckLeaseEvent.Set();
|
|
dataModel = iter->second->dataModel;
|
|
iter->second->notifyAliveConnection.disconnect();
|
|
jobs.erase(iter);
|
|
}
|
|
}
|
|
|
|
if (dataModel)
|
|
{
|
|
FASTLOGS(FLog::RCCServiceJobs, "Closing JobItem %s", jobID.c_str());
|
|
FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", dataModel.get());
|
|
|
|
closeDataModel(dataModel);
|
|
dataModel.reset();
|
|
if (::InterlockedDecrement(&dataModelCount) == 0)
|
|
mainLogManager->DisableHangReporting();
|
|
}
|
|
}
|
|
|
|
int jobCount() {
|
|
return jobs.size();
|
|
}
|
|
|
|
|
|
void getExpiration(const std::string& jobID, double * timeout)
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
*timeout = getJob(jobID)->second->secondsToTimeout();
|
|
}
|
|
|
|
void closeExpiredJobs(int* result)
|
|
{
|
|
std::vector<std::string> jobsToClose;
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
JobMap::iterator end = jobs.end();
|
|
for (JobMap::iterator iter = jobs.begin(); iter!=end; ++iter)
|
|
if (iter->second->secondsToTimeout()<=0)
|
|
jobsToClose.push_back(iter->first);
|
|
}
|
|
*result = jobsToClose.size();
|
|
std::for_each(jobsToClose.begin(), jobsToClose.end(), boost::bind(&CWebService::closeJob, this, _1, (const char*)NULL));
|
|
}
|
|
|
|
void closeAllJobs(int* result)
|
|
{
|
|
FASTLOG(FLog::RCCServiceJobs, "Closing all jobs command");
|
|
|
|
std::vector<std::string> jobsToClose;
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
JobMap::iterator end = jobs.end();
|
|
for (JobMap::iterator iter = jobs.begin(); iter!=end; ++iter)
|
|
jobsToClose.push_back(iter->first);
|
|
}
|
|
*result = jobsToClose.size();
|
|
std::for_each(jobsToClose.begin(), jobsToClose.end(), boost::bind(&CWebService::closeJob, this, _1, (const char*)NULL));
|
|
}
|
|
|
|
void getAllJobs(std::vector<ns1__Job * >& result, soap* soap)
|
|
{
|
|
boost::mutex::scoped_lock lock(sync);
|
|
result.resize(jobs.size());
|
|
|
|
int i = 0;
|
|
JobMap::iterator iter = jobs.begin();
|
|
JobMap::iterator end = jobs.end();
|
|
while (iter!=end)
|
|
{
|
|
ns1__Job* job = soap_new_ns1__Job(soap, -1);
|
|
job->expirationInSeconds = iter->second->secondsToTimeout();
|
|
job->category = iter->second->category;
|
|
job->cores = iter->second->cores;
|
|
job->id = iter->first;
|
|
result[i] = job;
|
|
++iter;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
|
|
boost::scoped_ptr<CWebService> CWebService::singleton;
|
|
|
|
void stop_CWebService()
|
|
{
|
|
CWebService::singleton.reset();
|
|
}
|
|
|
|
void start_CWebService(LPCTSTR contentpath, bool crashUploaderOnly)
|
|
{
|
|
if(PathIsRelative(contentpath))
|
|
{
|
|
TCHAR name[500];
|
|
::GetModuleFileName(_AtlBaseModule.m_hInst, name, 500);
|
|
CPath path = name;
|
|
path.RemoveFileSpec();
|
|
RBX::ContentProvider::setAssetFolder(path.m_strPath + "\\" + contentpath);
|
|
}
|
|
else
|
|
{
|
|
RBX::ContentProvider::setAssetFolder(contentpath);
|
|
}
|
|
|
|
CWebService::singleton.reset(new CWebService(crashUploaderOnly));
|
|
}
|
|
|
|
RBX_REGISTER_CLASS(ThumbnailGenerator);
|
|
|
|
void CWebService::collectPerfData()
|
|
{
|
|
while (::WaitForSingleObject(doneEvent.m_h, 1000)==WAIT_TIMEOUT) // used as an interruptible sleep.
|
|
s_perfCounter->CollectData();
|
|
}
|
|
|
|
CWebService::CWebService(bool crashUploadOnly) :
|
|
doneEvent(TRUE, FALSE),
|
|
dataModelCount(0)
|
|
{
|
|
RBX::StandardOut::singleton()->print(RBX::MESSAGE_INFO, "Intializing Roblox Web Service");
|
|
|
|
{
|
|
CVersionInfo vi;
|
|
vi.Load(_AtlBaseModule.m_hInst);
|
|
RBX::DebugSettings::robloxVersion = vi.GetFileVersionAsDotString();
|
|
|
|
RBX::Analytics::setReporter("RCCService");
|
|
RBX::Analytics::setAppVersion(vi.GetFileVersionAsString());
|
|
}
|
|
|
|
s_perfCounter = CProcessPerfCounter::getInstance();
|
|
s_profanityFilter = RBX::ProfanityFilter::getInstance();
|
|
perfData.reset(new boost::thread(RBX::thread_wrapper(boost::bind(&CWebService::collectPerfData, this), "CWebService::collectPerfData")));
|
|
|
|
RBX::Http::init(RBX::Http::WinHttp, RBX::Http::CookieSharingSingleProcessMultipleThreads);
|
|
RBX::Http::requester = "Server";
|
|
|
|
RBX::Profiling::init(false);
|
|
static RBX::FactoryRegistrator registerFactoryObjects; // this needs to be here so srand is called before rand
|
|
|
|
RBX::Analytics::InfluxDb::init(); // calls rand()
|
|
|
|
RobloxCrashReporter::silent = true;
|
|
mainLogManager->WriteCrashDump();
|
|
|
|
isThumbnailer = ::isServiceInstalled("Roblox.Thumbnails.Relay");
|
|
LoadAppSettings();
|
|
|
|
LoadClientSettings(rccSettings);
|
|
if (rccSettings.GetError())
|
|
{
|
|
RBXCRASH(rccSettings.GetErrorString().c_str());
|
|
}
|
|
|
|
RBX::TaskSchedulerSettings::singleton();
|
|
RBX::TaskScheduler::singleton().setThreadCount(RBX::TaskScheduler::ThreadPoolConfig(FInt::RCCServiceThreadCount));
|
|
|
|
// Force loading of settings classes
|
|
RBX::GameSettings::singleton();
|
|
RBX::LuaSettings::singleton();
|
|
RBX::DebugSettings::singleton();
|
|
RBX::PhysicsSettings::singleton();
|
|
|
|
RBX::Soundscape::SoundService::soundDisabled = true;
|
|
|
|
// Initialize the network code
|
|
RBX::Network::initWithServerSecurity();
|
|
|
|
//If crashUploadOnly = true, don't create a separate thread of control for uploading
|
|
static DumpErrorUploader dumpErrorUploader(!crashUploadOnly, "RCCService");
|
|
|
|
std::string dmpHandlerUrl = GetGridUrl(::GetBaseURL(), FFlag::UseDataDomain);
|
|
dumpErrorUploader.InitCrashEvent(dmpHandlerUrl, mainLogManager->getCrashEventName());
|
|
dumpErrorUploader.Upload(dmpHandlerUrl);
|
|
|
|
|
|
fetchClientSettingsThread.reset(new boost::thread(RBX::thread_wrapper(boost::bind(&CWebService::validateClientSettings, this), "CWebService::validateClientSettings")));
|
|
|
|
// load and set security versions immediately at start up so it's guaranteed to be there when server is launched
|
|
securityKeyUpdater.reset(new SecurityKeyUpdater(GetSecurityKeyUrl2(GetBaseURL(), "2b4ba7fc-5843-44cf-b107-ba22d3319dcd")));
|
|
md5Updater.reset(new MD5Updater(GetMD5HashUrl(GetBaseURL(), "2b4ba7fc-5843-44cf-b107-ba22d3319dcd")));
|
|
memHashUpdater.reset(new MemHashUpdater(GetMemHashUrl(GetBaseURL(), "2b4ba7fc-5843-44cf-b107-ba22d3319dcd")));
|
|
|
|
if (DFFlag::UseNewSecurityKeyApi)
|
|
{
|
|
securityKeyUpdater->run();
|
|
}
|
|
else
|
|
{
|
|
std::vector<std::string> versions = fetchAllowedSecurityVersions();
|
|
RBX::Network::setSecurityVersions(versions);
|
|
}
|
|
|
|
md5Updater->run();
|
|
|
|
if (DFFlag::UseNewMemHashApi)
|
|
{
|
|
if (DFString::MemHashConfig.size() < 3)
|
|
{
|
|
memHashUpdater->run();
|
|
}
|
|
else
|
|
{
|
|
RBX::Network::MemHashConfigs hashConfigs;
|
|
MemHashUpdater::populateMemHashConfigs(hashConfigs, DFString::MemHashConfig);
|
|
RBX::Network::Players::setGoldMemHashes(hashConfigs);
|
|
}
|
|
}
|
|
|
|
// this thread uses client setting values, so it must be started AFTER client settings are loaded
|
|
// create a thread to periodically check for security key changes
|
|
fetchSecurityDataThread.reset(new boost::thread(RBX::thread_wrapper(boost::bind(&CWebService::validateSecurityData, this), "CWebService::validateSecurityData")));
|
|
|
|
counters.reset(new CountersClient(GetBaseURL().c_str(), "76E5A40C-3AE1-4028-9F10-7C62520BD94F", NULL));
|
|
|
|
RBX::ViewBase::InitPluginModules();
|
|
|
|
if (DFFlag::US30476)
|
|
{
|
|
RBX::initAntiMemDump();
|
|
}
|
|
RBX::initLuaReadOnly();
|
|
RBX::initHwbpVeh();
|
|
|
|
if (FFlag::Dep)
|
|
{
|
|
typedef BOOL (WINAPI *SetProcessDEPPolicyPfn)(DWORD);
|
|
SetProcessDEPPolicyPfn pfn= reinterpret_cast<SetProcessDEPPolicyPfn>(GetProcAddress(GetModuleHandle("Kernel32"), "SetProcessDEPPolicy"));
|
|
if (pfn)
|
|
{
|
|
static const DWORD kEnable = 1;
|
|
pfn(kEnable);
|
|
}
|
|
}
|
|
}
|
|
|
|
CWebService::~CWebService()
|
|
{
|
|
doneEvent.Set();
|
|
perfData->join();
|
|
fetchSecurityDataThread->join();
|
|
fetchClientSettingsThread->join();
|
|
|
|
// TODO: this line crashes sometimes :-(
|
|
int result;
|
|
closeAllJobs(&result);
|
|
|
|
RBX::ViewBase::ShutdownPluginModules();
|
|
}
|
|
|
|
std::string CWebService::GetSettingsKey()
|
|
{
|
|
#ifdef RBX_TEST_BUILD
|
|
if (RCCServiceSettingsKeyOverwrite.length() > 0)
|
|
return RCCServiceSettingsKeyOverwrite;
|
|
#endif
|
|
|
|
if (settingsKey.length() == 0)
|
|
{
|
|
CRegKey key;
|
|
if (SUCCEEDED(key.Open(HKEY_LOCAL_MACHINE, "Software\\ROBLOX Corporation\\Roblox\\", KEY_READ)))
|
|
{
|
|
CHAR keyData[MAX_PATH];
|
|
ULONG bufLen = MAX_PATH-1;
|
|
if (SUCCEEDED(key.QueryStringValue("SettingsKey", keyData, &bufLen)))
|
|
{
|
|
keyData[bufLen] = 0;
|
|
settingsKey = std::string(keyData);
|
|
FASTLOGS(FLog::RCCServiceInit, "Read settings key: %s", settingsKey);
|
|
}
|
|
}
|
|
|
|
if (settingsKey.length() != 0)
|
|
settingsKey = "RCCService" + settingsKey;
|
|
}
|
|
|
|
return settingsKey;
|
|
}
|
|
void CWebService::LoadClientSettings(RCCServiceSettings& dest)
|
|
{
|
|
// cache settings in a string before processing
|
|
LoadClientSettings(clientSettingsData, thumbnailSettingsData);
|
|
LoadClientSettingsFromString(GetSettingsKey().c_str(), clientSettingsData, &dest);
|
|
if (isThumbnailer)
|
|
{
|
|
LoadClientSettingsFromString("RccThumbnailers", thumbnailSettingsData, &dest);
|
|
}
|
|
}
|
|
|
|
void CWebService::LoadClientSettings(std::string& clientDest, std::string& thumbnailDest)
|
|
{
|
|
clientDest.clear();
|
|
thumbnailDest.clear();
|
|
|
|
std::string key = GetSettingsKey();
|
|
if (key.length() != 0)
|
|
{
|
|
FetchClientSettingsData(key.c_str(), "D6925E56-BFB9-4908-AAA2-A5B1EC4B2D79", &clientDest);
|
|
}
|
|
if (isThumbnailer)
|
|
{
|
|
FetchClientSettingsData("RccThumbnailers", "D6925E56-BFB9-4908-AAA2-A5B1EC4B2D79", &thumbnailDest);
|
|
}
|
|
|
|
bool invalidClientSettings = (clientDest.empty() || key.length() == 0);
|
|
bool invalidThumbnailerSettings = (thumbnailDest.empty() && isThumbnailer);
|
|
if (invalidClientSettings || invalidThumbnailerSettings)
|
|
{
|
|
// hack: we want to log failure to load client settings in GA, but GA init require client setting...
|
|
// so just init GA with default settings, which points to "test" account
|
|
RBX::RobloxGoogleAnalytics::setCanUseAnalytics();
|
|
RBX::RobloxGoogleAnalytics::init(rccSettings.GetValueGoogleAnalyticsAccountPropertyID(), rccSettings.GetValueGoogleAnalyticsThreadPoolMaxScheduleSize());
|
|
|
|
TCHAR computerName[1024];
|
|
DWORD size = 1024;
|
|
GetComputerName(computerName, &size);
|
|
|
|
if (invalidClientSettings)
|
|
{
|
|
RBX::RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_ERROR, key.length() ? "Empty settings string" : "Empty settings key", computerName);
|
|
}
|
|
else
|
|
{
|
|
RBX::RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_ERROR, "Empty thumbnailer settings string", computerName);
|
|
}
|
|
|
|
if (DFFlag::DebugCrashOnFailToLoadClientSettings)
|
|
RBXCRASH();
|
|
}
|
|
}
|
|
|
|
void CWebService::LoadAppSettings()
|
|
{
|
|
boost::filesystem::path exePath = RBX::FileSystem::getUserDirectory(false, RBX::DirExe, NULL);
|
|
|
|
FASTLOGS(FLog::RCCServiceInit, "Loading AppSettings.xml, current path: %s", exePath.string());
|
|
|
|
std::ifstream stream((exePath / "AppSettings.xml").native().c_str());
|
|
|
|
TextXmlParser machine(stream.rdbuf());
|
|
std::auto_ptr<XmlElement> root = machine.parse();
|
|
|
|
const XmlElement* baseURLNode = root->findFirstChildByTag(RBX::Name::declare("BaseUrl"));
|
|
if(baseURLNode)
|
|
{
|
|
std::string baseURL;
|
|
baseURLNode->getValue(baseURL);
|
|
FASTLOGS(FLog::RCCServiceInit, "Got Base url: %s", baseURL);
|
|
SetBaseURL(baseURL);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
std::vector<std::string> CWebService::fetchAllowedSecurityVersions()
|
|
{
|
|
const std::string& baseUrl = GetBaseURL();
|
|
if (baseUrl.size()==0)
|
|
RBXCRASH(); // y u no set BaseURL??
|
|
|
|
std::vector<std::string> versions;
|
|
|
|
std::string url = GetSecurityKeyUrl(baseUrl, "2b4ba7fc-5843-44cf-b107-ba22d3319dcd");
|
|
|
|
RBX::Http request(url);
|
|
|
|
std::string allowedSecurityVerionsData = "";
|
|
try
|
|
{
|
|
request.get(allowedSecurityVerionsData);
|
|
|
|
// no need to continue if security version did not change
|
|
if (allowedSecurityVerionsData == securityVersionData)
|
|
return std::vector<std::string>();
|
|
|
|
// parse json data
|
|
boost::property_tree::ptree pt;
|
|
std::stringstream stream(allowedSecurityVerionsData);
|
|
read_json(stream, pt);
|
|
|
|
BOOST_FOREACH(const boost::property_tree::ptree::value_type& child, pt.get_child("data"))
|
|
{
|
|
// version = data + salt
|
|
std::string version = RBX::sha1(child.second.data() + "askljfLUZF");
|
|
versions.push_back(version);
|
|
}
|
|
|
|
securityVersionData = allowedSecurityVerionsData;
|
|
|
|
}
|
|
catch (std::exception ex)
|
|
{
|
|
FASTLOG(FLog::Always, "LoadAllowedPlayerVersions exception");
|
|
}
|
|
|
|
return versions;
|
|
}
|
|
|
|
void CWebService::JobItem::touch(double seconds)
|
|
{
|
|
expirationTime = RBX::Time::now<RBX::Time::Fast>() + RBX::Time::Interval(seconds);
|
|
}
|
|
|
|
double CWebService::JobItem::secondsToTimeout() const
|
|
{
|
|
return (expirationTime - RBX::Time::now<RBX::Time::Fast>()).seconds();
|
|
}
|
|
|
|
|
|
int RCCServiceSoapService::HelloWorld(_ns1__HelloWorld *ns1__HelloWorld, _ns1__HelloWorldResponse *ns1__HelloWorldResponse)
|
|
{
|
|
BEGIN_PRINT(HelloWorld,"HelloWorld");
|
|
|
|
::InterlockedIncrement(&helloWorldCount);
|
|
ns1__HelloWorldResponse->HelloWorldResult = soap_new_std__string(this, -1);
|
|
*ns1__HelloWorldResponse->HelloWorldResult = "Hello World";
|
|
::InterlockedDecrement(&helloWorldCount);
|
|
END_PRINT(HelloWorld,"HelloWorld");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::Diag(_ns1__Diag *ns1__Diag, _ns1__DiagResponse *ns1__DiagResponse)
|
|
{
|
|
BEGIN_PRINT(Diag,"Diag");
|
|
|
|
::InterlockedIncrement(&diagCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->diag(ns1__Diag->type, *ns1__Diag->jobID, &ns1__DiagResponse->DiagResult, this);
|
|
::InterlockedDecrement(&diagCount);
|
|
|
|
END_PRINT(Diag,"Diag");
|
|
return 0;
|
|
}
|
|
int RCCServiceSoapService::DiagEx(_ns1__DiagEx *ns1__DiagEx, _ns1__DiagExResponse *ns1__DiagExResponse)
|
|
{
|
|
BEGIN_PRINT(DiagEx,"DiagEx");
|
|
::InterlockedIncrement(&diagCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
ns1__DiagExResponse->DiagExResult = soap_new_ns1__ArrayOfLuaValue(this, -1);
|
|
CWebService::singleton->diag(ns1__DiagEx->type, *ns1__DiagEx->jobID, &ns1__DiagExResponse->DiagExResult->LuaValue, this);
|
|
::InterlockedDecrement(&diagCount);
|
|
END_PRINT(DiagEx,"DiagEx");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::GetVersion(_ns1__GetVersion *ns1__GetVersion, _ns1__GetVersionResponse *ns1__GetVersionResponse)
|
|
{
|
|
BEGIN_PRINT(GetVersion,"GetVersion");
|
|
::InterlockedIncrement(&getVersionCount);
|
|
ns1__GetVersionResponse->GetVersionResult = RBX::DebugSettings::robloxVersion.c_str();
|
|
::InterlockedDecrement(&getVersionCount);
|
|
END_PRINT(GetVersion,"GetVersion");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::GetStatus(_ns1__GetStatus *ns1__GetStatus, _ns1__GetStatusResponse *ns1__GetStatusResponse)
|
|
{
|
|
BEGIN_PRINT(GetStatus,"GetStatus");
|
|
|
|
::InterlockedIncrement(&getStatusCount);
|
|
ns1__GetStatusResponse->GetStatusResult = soap_new_ns1__Status(this, -1);
|
|
ns1__GetStatusResponse->GetStatusResult->version = soap_new_std__string(this, -1);
|
|
*ns1__GetStatusResponse->GetStatusResult->version = RBX::DebugSettings::robloxVersion.c_str();
|
|
ns1__GetStatusResponse->GetStatusResult->environmentCount = CWebService::singleton->jobCount();
|
|
::InterlockedDecrement(&getStatusCount);
|
|
END_PRINT(GetStatus,"GetStatus");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::OpenJob(_ns1__OpenJob *ns1__OpenJob, _ns1__OpenJobResponse *ns1__OpenJobResponse)
|
|
{
|
|
BEGIN_PRINT(OpenJob,"OpenJob");
|
|
::InterlockedIncrement(&openJobCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->openJob(*ns1__OpenJob->job, ns1__OpenJob->script, &ns1__OpenJobResponse->OpenJobResult, this, true);
|
|
::InterlockedDecrement(&openJobCount);
|
|
END_PRINT(OpenJob,"OpenJob");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::OpenJobEx(_ns1__OpenJobEx *ns1__OpenJobEx, _ns1__OpenJobExResponse *ns1__OpenJobExResponse)
|
|
{
|
|
BEGIN_PRINT(OpenJobEx,"OpenJobEx");
|
|
|
|
::InterlockedIncrement(&openJobCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
ns1__OpenJobExResponse->OpenJobExResult = soap_new_ns1__ArrayOfLuaValue(this, -1);
|
|
CWebService::singleton->openJob(*ns1__OpenJobEx->job, ns1__OpenJobEx->script, &ns1__OpenJobExResponse->OpenJobExResult->LuaValue, this, true);
|
|
::InterlockedDecrement(&openJobCount);
|
|
|
|
END_PRINT(OpenJobEx,"OpenJobEx");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::Execute(_ns1__Execute *ns1__Execute, _ns1__ExecuteResponse *ns1__ExecuteResponse)
|
|
{
|
|
BEGIN_PRINT(Execute,"Execute");
|
|
|
|
::InterlockedIncrement(&executeCount);
|
|
try
|
|
{
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->execute(ns1__Execute->jobID, ns1__Execute->script, &ns1__ExecuteResponse->ExecuteResult, this);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&executeCount);
|
|
throw;
|
|
}
|
|
::InterlockedDecrement(&executeCount);
|
|
END_PRINT(Execute,"Execute");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::ExecuteEx(_ns1__ExecuteEx *ns1__ExecuteEx, _ns1__ExecuteExResponse *ns1__ExecuteExResponse)
|
|
{
|
|
BEGIN_PRINT(ExecuteEx,"ExecuteEx");
|
|
|
|
::InterlockedIncrement(&executeCount);
|
|
try
|
|
{
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
ns1__ExecuteExResponse->ExecuteExResult = soap_new_ns1__ArrayOfLuaValue(this, -1);
|
|
CWebService::singleton->execute(ns1__ExecuteEx->jobID, ns1__ExecuteEx->script, &ns1__ExecuteExResponse->ExecuteExResult->LuaValue, this);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&executeCount);
|
|
throw;
|
|
}
|
|
|
|
::InterlockedDecrement(&executeCount);
|
|
END_PRINT(ExecuteEx,"ExecuteEx");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int RCCServiceSoapService::CloseJob(_ns1__CloseJob *ns1__CloseJob, _ns1__CloseJobResponse *ns1__CloseJobResponse)
|
|
{
|
|
BEGIN_PRINT(CloseJob,"CloseJob");
|
|
::InterlockedIncrement(&closeJobCount);
|
|
try
|
|
{
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->closeJob(ns1__CloseJob->jobID);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&closeJobCount);
|
|
throw;
|
|
}
|
|
::InterlockedDecrement(&closeJobCount);
|
|
END_PRINT(CloseJob,"CloseJob");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::RenewLease(_ns1__RenewLease *ns1__RenewLease, _ns1__RenewLeaseResponse *ns1__RenewLeaseResponse)
|
|
{
|
|
BEGIN_PRINT(RenewLease,"RenewLease");
|
|
|
|
::InterlockedIncrement(&renewLeaseCount);
|
|
try
|
|
{
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->renewLease(ns1__RenewLease->jobID, ns1__RenewLease->expirationInSeconds);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&renewLeaseCount);
|
|
throw;
|
|
}
|
|
::InterlockedDecrement(&renewLeaseCount);
|
|
END_PRINT(RenewLease,"RenewLease");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::BatchJob(_ns1__BatchJob *ns1__BatchJob, _ns1__BatchJobResponse *ns1__BatchJobResponse)
|
|
{
|
|
BEGIN_PRINT(BatchJob,"BatchJob");
|
|
|
|
::InterlockedIncrement(&batchJobCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
// Batch jobs are completed synchronously, so there is no need to start the heartbeat
|
|
try
|
|
{
|
|
CWebService::singleton->batchJob(*ns1__BatchJob->job, ns1__BatchJob->script, &ns1__BatchJobResponse->BatchJobResult, this);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&batchJobCount);
|
|
throw;
|
|
}
|
|
::InterlockedDecrement(&batchJobCount);
|
|
END_PRINT(BatchJob,"BatchJob");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int RCCServiceSoapService::BatchJobEx(_ns1__BatchJobEx *ns1__BatchJobEx, _ns1__BatchJobExResponse *ns1__BatchJobExResponse)
|
|
{
|
|
BEGIN_PRINT(BatchJobEx,"BatchJobEx");
|
|
|
|
::InterlockedIncrement(&batchJobCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
ns1__BatchJobExResponse->BatchJobExResult = soap_new_ns1__ArrayOfLuaValue(this, -1);
|
|
// Batch jobs are completed synchronously, so there is no need to start the heartbeat
|
|
try
|
|
{
|
|
CWebService::singleton->batchJob(*ns1__BatchJobEx->job, ns1__BatchJobEx->script, &ns1__BatchJobExResponse->BatchJobExResult->LuaValue, this);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&batchJobCount);
|
|
throw;
|
|
}
|
|
|
|
::InterlockedDecrement(&batchJobCount);
|
|
END_PRINT(BatchJobEx,"BatchJobEx");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::GetExpiration(_ns1__GetExpiration *ns1__GetExpiration, _ns1__GetExpirationResponse *ns1__GetExpirationResponse)
|
|
{
|
|
BEGIN_PRINT(GetExpiration,"GetExpiration");
|
|
|
|
::InterlockedIncrement(&getExpirationCount);
|
|
try{
|
|
CWebService::singleton->getExpiration(ns1__GetExpiration->jobID, &ns1__GetExpirationResponse->GetExpirationResult);
|
|
}
|
|
catch(std::exception&)
|
|
{
|
|
::InterlockedDecrement(&getExpirationCount);
|
|
throw;
|
|
}
|
|
::InterlockedDecrement(&getExpirationCount);
|
|
END_PRINT(GetExpiration,"GetExpiration");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::GetAllJobs(_ns1__GetAllJobs *ns1__GetAllJobs, _ns1__GetAllJobsResponse *ns1__GetAllJobsResponse)
|
|
{
|
|
BEGIN_PRINT(GetAllJobs,"GetAllJobs");
|
|
|
|
::InterlockedIncrement(&getAllJobsCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->getAllJobs(ns1__GetAllJobsResponse->GetAllJobsResult, this);
|
|
::InterlockedDecrement(&getAllJobsCount);
|
|
END_PRINT(GetAllJobs,"GetAllJobs");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::GetAllJobsEx(_ns1__GetAllJobsEx *ns1__GetAllJobsEx, _ns1__GetAllJobsExResponse *ns1__GetAllJobsExResponse)
|
|
{
|
|
BEGIN_PRINT(GetAllJobsEx,"GetAllJobsEx");
|
|
|
|
::InterlockedIncrement(&getAllJobsCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
ns1__GetAllJobsExResponse->GetAllJobsExResult = soap_new_ns1__ArrayOfJob(this, -1);
|
|
CWebService::singleton->getAllJobs(ns1__GetAllJobsExResponse->GetAllJobsExResult->Job, this);
|
|
::InterlockedDecrement(&getAllJobsCount);
|
|
END_PRINT(GetAllJobsEx,"GetAllJobsEx");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::CloseExpiredJobs(_ns1__CloseExpiredJobs *ns1__CloseExpiredJobs, _ns1__CloseExpiredJobsResponse *ns1__CloseExpiredJobsResponse)
|
|
{
|
|
BEGIN_PRINT(GetAllJobs,"CloseExpiredJobs");
|
|
|
|
::InterlockedIncrement(&closeExpiredJobsCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->closeExpiredJobs(&ns1__CloseExpiredJobsResponse->CloseExpiredJobsResult);
|
|
::InterlockedDecrement(&closeExpiredJobsCount);
|
|
END_PRINT(GetAllJobs,"CloseExpiredJobs");
|
|
return 0;
|
|
}
|
|
|
|
int RCCServiceSoapService::CloseAllJobs(_ns1__CloseAllJobs *ns1__CloseAllJobs, _ns1__CloseAllJobsResponse *ns1__CloseAllJobsResponse)
|
|
{
|
|
BEGIN_PRINT(GetAllJobs,"CloseAllJobs");
|
|
|
|
::InterlockedIncrement(&closeAllJobsCount);
|
|
RBX::Security::Impersonator impersonate(RBX::Security::WebService);
|
|
CWebService::singleton->closeAllJobs(&ns1__CloseAllJobsResponse->CloseAllJobsResult);
|
|
::InterlockedDecrement(&closeAllJobsCount);
|
|
END_PRINT(GetAllJobs,"CloseAllJobs");
|
|
return 0;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
static bool isServiceInstalled(const char* svcName)
|
|
{
|
|
bool result = false;
|
|
SC_HANDLE scm = OpenSCManager(NULL, NULL, NULL);
|
|
SC_HANDLE thumbService = OpenService(scm, svcName, SERVICE_QUERY_STATUS);
|
|
if (thumbService)
|
|
{
|
|
result = true;
|
|
CloseServiceHandle(thumbService);
|
|
}
|
|
if (scm)
|
|
{
|
|
CloseServiceHandle(scm);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|