From 46d30d0ca2b32547d75133e5f63999a5e721caf9 Mon Sep 17 00:00:00 2001 From: guy Date: Mon, 25 Jan 2010 15:36:05 +0000 Subject: [PATCH] Patch level : 10.0 Files correlati : lurch.exe Ricompilazione Demo : [ ] Commento : Questo maggiordomo non solo e' in grado di ammazzare la servitu' oziosa... ma anche di risuscitarla quanto questa si suicida! git-svn-id: svn://10.65.10.50/trunk@19979 c028cbd2-c16b-5b4b-a496-9718f37d4682 --- server/baseserv.cpp | 22 ++- server/baseserv.h | 11 +- server/lurch.cpp | 416 ++++++++++++++++++++++++-------------------- server/xml.cpp | 1 + 4 files changed, 253 insertions(+), 197 deletions(-) diff --git a/server/baseserv.cpp b/server/baseserv.cpp index 9eb6be60e..970658304 100755 --- a/server/baseserv.cpp +++ b/server/baseserv.cpp @@ -15,7 +15,7 @@ // -------------------------------------------------------------------------- // headers // -------------------------------------------------------------------------- - +#include #include "baseserv.h" #include @@ -25,12 +25,10 @@ #include #include -#ifdef WIN32 +#ifndef __WXMSW__ #include #endif -#include - /////////////////////////////////////////////////////////// // Utilities /////////////////////////////////////////////////////////// @@ -589,6 +587,11 @@ const wxString& TBaseServerApp::GetConfigName() const void TBaseServerApp::SetConfigString(const wxChar* key, const wxChar* val, const wxChar* app) const { +#ifdef __WXMSW__ + if (app == NULL || *app == '\0') + app = GetAppName(); + ::WritePrivateProfileString(app, key, val, GetConfigName()); +#else wxFileConfig ini("", "", GetConfigName(), "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); wxString str; if (app == NULL || *app == '\0') @@ -596,6 +599,7 @@ void TBaseServerApp::SetConfigString(const wxChar* key, const wxChar* val, const str << '/' << app; ini.SetPath(str); ini.Write(key, val); +#endif } void TBaseServerApp::SetConfigInt(const wxChar* key, int val, const wxChar* app) const @@ -606,13 +610,19 @@ void TBaseServerApp::SetConfigInt(const wxChar* key, int val, const wxChar* app) wxString TBaseServerApp::GetConfigString(const wxChar* key, const wxChar* def, const wxChar* app) const { - wxFileConfig ini("", "", GetConfigName(), "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); wxString str; - if (app == NULL || *app == '\0') + if (app == NULL || *app == '\0') app = GetAppName(); +#ifdef __WXMSW__ + wxChar value[512]; memset(value, 0, sizeof(value)); + const int len = ::GetPrivateProfileString(app, key, def, value, sizeof(value)-1, GetConfigName()); + str = len <= 0 ? def : value; +#else + wxFileConfig ini("", "", GetConfigName(), "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); str << '/' << app; ini.SetPath(str); ini.Read(key, &str, def); +#endif return str; } diff --git a/server/baseserv.h b/server/baseserv.h index c2127595d..1b6b513cc 100755 --- a/server/baseserv.h +++ b/server/baseserv.h @@ -1,11 +1,12 @@ -#include +#ifndef __BASESERV_H__ +#define __BASESERV_H__ + +#include #include +#include #include #include #include -#ifdef LINUX -#include -#endif #include "xml.h" @@ -150,3 +151,5 @@ public: }; TBaseServerApp& GetServerApp(); + +#endif diff --git a/server/lurch.cpp b/server/lurch.cpp index ddc428bb6..0abe95cd8 100755 --- a/server/lurch.cpp +++ b/server/lurch.cpp @@ -3,56 +3,36 @@ #include #include #include -#ifdef WIN32 -#include -#endif - -#include - -/////////////////////////////////////////////////////////// -// TProcess declaration -/////////////////////////////////////////////////////////// - -class TLurchServer; //segnaposto della TLurchServer che serve alla TProcess - -class TProcess : public wxProcess -{ - TLurchServer* m_pLurch; - wxString m_strApp; - -protected: - virtual void OnTerminate(int pid, int status); - -public: - void ForcePid(int pid) { SetPid(pid); } - TProcess(TLurchServer* pLurch, const wxString& strApp) : m_pLurch(pLurch), m_strApp(strApp) {} -}; +#include /////////////////////////////////////////////////////////// // TLurchServer declaration /////////////////////////////////////////////////////////// -//classe TProcessHashMap derivata da wxHashMap (è una specie di assocarray!) +//classe TProcessHashMap derivata da wxHashMap (è l'equivalente di TAssoc_array) WX_DECLARE_HASH_MAP( wxString, wxProcess*, wxStringHash, wxStringEqual, TProcessHashMap ); - class TLurchServer : public TBaseServerApp { - TProcessHashMap m_ProcMap; - wxTimer m_Timer; - int m_nFreq; + TProcessHashMap m_hmProcMap; + wxSemaphore m_Semaphore; + wxTimer m_PingTimer; + wxArrayString m_aServers; DECLARE_EVENT_TABLE(); + protected: virtual const wxChar* GetAppName() const; virtual bool Initialization(); void OnTimer(wxTimerEvent& evt); - bool RestartTimer(); + void OnEndProcess(wxProcessEvent& evt); - void AddMiniForm(TXmlItem& tr, const wxChar* action, const wxChar* app, const wxChar* prompt) const; + TXmlItem& AddMiniForm(TXmlItem& tr, const wxChar* action, const wxChar* app, const wxChar* prompt) const; void EnumerateVariables(const wxString& strApp, wxArrayString& arr) const; - void CreateServersList(wxArrayString& arr) const; + const wxArrayString& GetServersList(); + const TProcessHashMap& GetRunningServers() { return m_hmProcMap; } + bool PingProcess(const wxString& strApp); void StopProcess(const wxString& strApp); wxString StartProcess(const wxString& strApp); @@ -70,116 +50,150 @@ public: //metodi riguardanti l'interfaccia html void ProcessFormStart(const THashTable& args, wxSocketBase& sock); void ProcessFormStop(const THashTable& args, wxSocketBase& sock); + void ProcessFormRestart(const THashTable& args, wxSocketBase& sock); void ProcessFormConfig(const THashTable& args, wxSocketBase& sock); void ProcessFormUpdate(THashTable& args, wxSocketBase& sock); void CallCgi(wxString& strFileName, wxSocketBase& sock); + + TLurchServer(); }; -/////////////////////////////////////////////////////////// -// TProcess implementation -/////////////////////////////////////////////////////////// - -void TProcess::OnTerminate(int pid, int WXUNUSED(status)) -{ - SetPid(pid); - m_pLurch->ForgetProcess(m_strApp); -} - -/////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////// // TLurchServer implementation /////////////////////////////////////////////////////////// BEGIN_EVENT_TABLE(TLurchServer, TBaseServerApp) -EVT_TIMER(wxID_ANY, TLurchServer::OnTimer) + EVT_TIMER(wxID_ANY, TLurchServer::OnTimer) + EVT_END_PROCESS(wxID_ANY, TLurchServer::OnEndProcess) END_EVENT_TABLE() +#ifndef max +#define max(a,b) ((a)>(b) ? (a) : (b)) +#endif + bool TLurchServer::PingProcess(const wxString& strApp) { bool bPinged = false; - TProcess* pProcess = (TProcess*)m_ProcMap[strApp]; - if (pProcess != NULL) + + const wxString strPort = GetConfigString("Port", "", strApp); + if (!strPort.IsEmpty()) { - const wxString strPort = GetConfigString("Port", "", strApp); - if (!strPort.IsEmpty()) + const int nInterval = m_PingTimer.GetInterval(); // msec + const int nTimeOut = max(nInterval/4, 250); // msec + wxIPV4address ipAddress; + ipAddress.LocalHost(); + ipAddress.Service(strPort); + wxSocketClient sSock(wxSOCKET_NOWAIT); + sSock.Connect(ipAddress, false); + if (sSock.WaitOnConnect(0, nTimeOut)) { - const int nTimeOut = m_nFreq > 3000 ? m_nFreq / 3000 : 1; - wxIPV4address ipAddress; - ipAddress.LocalHost(); - ipAddress.Service(strPort); - wxSocketClient sSock(wxSOCKET_NOWAIT); - sSock.Connect(ipAddress, false); - if (sSock.WaitOnConnect(nTimeOut)) + sSock.Discard(); // Tralascia eventuali dati in arrivo non richiesti + const wxString strPing = wxT("PING\n"); + sSock.Write(strPing, strPing.Len()); + if (sSock.WaitForWrite(0, nTimeOut)) { - sSock.Write("PING\n", 5); - if (sSock.WaitForWrite(nTimeOut)) - { - char buffer[8]; memset(buffer, 0, sizeof(buffer)); - sSock.Read(buffer, 4); - if (sSock.WaitForRead(nTimeOut)) - bPinged = wxStrcmp(buffer, "PONG") == 0; - } + ::wxMilliSleep(nTimeOut); // Triste necessita' :-( Aspetto qui ... + char buffer[8]; memset(buffer, 0, sizeof(buffer)); + sSock.Read(buffer, 4); + if (sSock.WaitForRead(0, 1)) // ... ma poi non aspetto qua :-) + bPinged = wxStrncmp(buffer, "PONG", 4) == 0; + sSock.Discard(); // Tralascia ulteriori dati in arrivo } } } + return bPinged; } -void TLurchServer::OnTimer(wxTimerEvent& WXUNUSED(evt)) +void TLurchServer::OnTimer(wxTimerEvent& evt) { - m_Timer.Stop(); - const wxString strApp = "Authorization"; - if (!PingProcess(strApp)) + if (m_Semaphore.TryWait() != wxSEMA_NO_ERROR) { - TProcess* pProcess = (TProcess*)m_ProcMap[strApp]; - wxKillError ke = pProcess->Kill(pProcess->GetPid()); - if (ke == wxKILL_OK) - WriteLog(strApp + " killed!"); - else - { - wxString strMsg; - strMsg << "Error " << ke << " killing " << strApp; - WriteLog(strMsg); - } - wxSleep(2); - StartProcess(strApp); + evt.Skip(); + return; + } + + const TProcessHashMap& hmServers = GetRunningServers(); + const int nServers = hmServers.size(); + if (nServers > 0) // Is anybody out there? + { + const int nRandomIndex = rand() % nServers; + wxString strApp; // Usually "Authorization" + TProcessHashMap::const_iterator it; + for (it = hmServers.begin(); it != hmServers.end(); ++it) + strApp = it->first; + + if (!PingProcess(strApp)) + { + wxProcess* pProcess = m_hmProcMap[strApp]; + wxKillError ke = pProcess->Kill(pProcess->GetPid()); + if (ke == wxKILL_OK) + WriteLog(strApp + _(" killed!")); + else + { + wxString strMsg; + strMsg << _("Error ") << ke << _(" killing ") << strApp; + WriteLog(strMsg); + } + wxSleep(2); + StartProcess(strApp); + } + } + + m_Semaphore.Post(); +} + +void TLurchServer::OnEndProcess(wxProcessEvent& evt) +{ + TProcessHashMap::const_iterator it; + for (it = m_hmProcMap.begin(); it != m_hmProcMap.end(); ++it) + { + const wxProcess* p = it->second; + if (p->GetPid() == evt.GetPid()) + { + ForgetProcess(it->first); + break; + } } - RestartTimer(); } const wxChar* TLurchServer::GetAppName() const +{ return wxT("Lurch"); } + +const wxArrayString& TLurchServer::GetServersList() { - return "Lurch"; + if (m_aServers.IsEmpty()) + { + wxFileInputStream ini(GetConfigName()); + const size_t size = ini.GetSize(); + wxChar* buff = new wxChar[size]; + ini.Read(buff, size); + + const char* aperta = strchr(buff, '['); + while (aperta != NULL) + { + char* chiusa = (char*)strchr(aperta+1, ']'); + if (chiusa != NULL) + { + *chiusa = '\0'; + wxString str = aperta+1; + m_aServers.Add(str); + aperta = strchr(chiusa+1, '['); + } + else + break; + } + } + return m_aServers; } -void TLurchServer::CreateServersList(wxArrayString& arr) const +TXmlItem& TLurchServer::AddMiniForm(TXmlItem& tr, const wxChar* action, const wxChar* app, const wxChar* prompt) const { - wxFileInputStream ini(GetConfigName()); - const size_t size = ini.GetSize(); - wxChar* buff = new wxChar[size]; - ini.Read(buff, size); - - const char* aperta = strchr(buff, '['); - while (aperta != NULL) - { - char* chiusa = (char*)strchr(aperta+1, ']'); - if (chiusa != NULL) - { - *chiusa = '\0'; - wxString str = aperta+1; - arr.Add(str); - aperta = strchr(chiusa+1, '['); - } - else - break; - } - - delete buff; -} - -void TLurchServer::AddMiniForm(TXmlItem& tr, const wxChar* action, const wxChar* app, const wxChar* prompt) const -{ - TXmlItem& td = tr.AddChild("td").SetAttr("width", "15%"); - TXmlItem& form = td.AddChild("center").AddChild("form"); + TXmlItem& td = tr.AddChild("td").SetAttr("width", "10%"); + td.SetAttr("align", "center"); + td.SetAttr("valign", "center"); // Lo ignora ma insisto + + TXmlItem& form = td.AddChild("form"); form.SetAttr("action", action); TXmlItem& name = form.AddChild("input"); @@ -189,6 +203,7 @@ void TLurchServer::AddMiniForm(TXmlItem& tr, const wxChar* action, const wxChar* TXmlItem& submit = form.AddChild("input"); submit.SetAttr("type", "submit"); submit.SetAttr("value", prompt); + return submit; } void TLurchServer::GenerateFile(wxString& strFilename) @@ -203,30 +218,21 @@ void TLurchServer::GenerateFile(wxString& strFilename) strFilename = GetTempFilename(); const wxString strLurchName = GetAppName(); - wxArrayString arr; CreateServersList(arr); + const wxArrayString& arr = GetServersList(); for (size_t i = 0; i < arr.GetCount(); i++) { - const bool bLurch = arr[i] == strLurchName; - wxFileConfig ini("", "", GetConfigName(), "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); - wxString str; - str << '/' << arr[i]; - ini.SetPath(str); + const wxString& strApp = arr[i]; + const bool bLurch = strApp == strLurchName; + const wxString strHost = GetConfigString("Host", "127.0.0.1", strApp); + const int nPort = GetConfigInt("Port", 3883, strApp); + const wxString strIcon = GetConfigString("Icon", "euro.gif", strApp); - wxString strHost; - ini.Read("Host", &strHost, "127.0.0.1"); - - int nPort; - ini.Read("Port", &nPort, 3883); - - wxString strIcon; - ini.Read("Icon", &strIcon, "euro.gif"); - - const wxSingleInstanceChecker sic(arr[i]); + const wxSingleInstanceChecker sic(strApp); const bool bRunning = sic.IsAnotherRunning(); TXmlItem& tr = table.AddChild("tr"); TXmlItem& td0 = tr.AddChild("td"); - td0.SetAttr("width", "15%"); td0.SetAttr("align", "center"); + td0.SetAttr("width", "10%"); td0.SetAttr("align", "center"); TXmlItem& a = td0.AddChild("a"); if (!bLurch && bRunning) { @@ -236,8 +242,17 @@ void TLurchServer::GenerateFile(wxString& strFilename) TXmlItem& img = a.AddChild("img"); img.SetAttr("src", strIcon); img.SetAttr("border", 0L); img.SetAttr("alt", arr[i]); - AddMiniForm(tr, (bRunning || bLurch) ? "kill.cgi" : "start.cgi", arr[i], (bRunning || bLurch) ? "Stop" : "Start"); - AddMiniForm(tr, "config.cgi", arr[i], "Configure"); + TXmlItem& buttStart = AddMiniForm(tr, "start.cgi", arr[i], _("Start")); + if (bRunning || bLurch) + buttStart.SetAttr(wxT("type"), wxT("hidden")); + + TXmlItem& buttStop = AddMiniForm(tr, "kill.cgi", arr[i], _("Stop")); + if (!(bRunning || bLurch)) + buttStop.SetAttr(wxT("type"), wxT("hidden")); + + TXmlItem& buttRestart = AddMiniForm(tr, "restart.cgi", arr[i], _("Restart")); + if (!(bRunning && !bLurch)) + buttRestart.SetAttr(wxT("type"), wxT("hidden")); TXmlItem& a3 = tr.AddChild("td").AddChild("a"); if (!bLurch && bRunning) @@ -264,7 +279,7 @@ void TLurchServer::GenerateFile(wxString& strFilename) TXmlItem& tr2 = panel.AddChild("tr"); tr2.AddChild("td").AddEnclosedText("Ping Frequency"); - wxString strFreq; strFreq << m_nFreq/1000; + wxString strFreq; strFreq << m_PingTimer.GetInterval()/1000; tr2.AddChild("td").AddEnclosedText(strFreq); html.Save(strFilename); @@ -301,22 +316,6 @@ bool TLurchServer::IsCgiName(wxString strFilename) const return strExt == "cgi" || strExt == "exe"; } -bool TLurchServer::RestartTimer() -{ - bool ok = true; - if (m_nFreq > 0) - { - ok = m_Timer.Start(m_nFreq) && m_Timer.IsRunning(); - if (!ok) - { - wxString strMsg; - strMsg << "Error starting timer with " << m_nFreq << " ms frequency"; - WriteLog(strMsg); - } - } - return ok; -} - wxString TLurchServer::StartProcess(const wxString& strApp) { bool ok = false; @@ -337,28 +336,26 @@ wxString TLurchServer::StartProcess(const wxString& strApp) fnPath.SetPath(GetServerPath()); fnPath.SetFullName(strRun); - TProcess* pProcess = new TProcess(this, strApp); - const long nProc = wxExecute(fnPath.GetFullPath(), wxEXEC_ASYNC, pProcess); - if (nProc == 0 || nProc == -1) + wxProcess* pProcess = wxProcess::Open(fnPath.GetFullPath()); + if (pProcess == NULL) { - strMessage.Printf("Can't run %s executable (%s)", strApp.c_str(), strRun.c_str()); - delete pProcess; + strMessage.Printf(_("Can't run %s executable (%s)"), strApp.c_str(), strRun.c_str()); } else { - pProcess->ForcePid(nProc); - m_ProcMap[strApp] = pProcess; //memorizza il numero del processo che ha lanciato per poterlo usare in fase di controllo + m_hmProcMap[strApp] = pProcess; // memorizza il numero del processo che ha lanciato per poterlo usare in fase di controllo strMessage = wxEmptyString; + ::wxSleep(1); // Lascia il tempo di inizializzarsi prima di compinciare a pingarlo! } } else - strMessage.Printf("Can't find %s executable (%s)", strApp.c_str(), strRun.c_str()); + strMessage.Printf(_("Can't find %s executable (%s)"), strApp.c_str(), strRun.c_str()); } else - strMessage.Printf("%s il already running", strApp.c_str()); + strMessage.Printf(_("%s il already running"), strApp.c_str()); if (strMessage.IsEmpty()) - WriteLog(strApp + " started."); + WriteLog(strApp + _(" started.")); else WriteLog(strMessage); @@ -372,16 +369,16 @@ void TLurchServer::ProcessFormStart(const THashTable& args, wxSocketBase& sock) { const wxString strMessage = StartProcess(strApp); if (strMessage.IsEmpty()) - MessageBox("Server Started", strApp, sock); + MessageBox(_("Server Started"), strApp, sock); else - MessageBox("ERROR", strMessage, sock); + MessageBox(_("ERROR"), strMessage, sock); } } bool TLurchServer::ForgetProcess(const wxString& strApp) { - WriteLog(strApp + " terminated."); - return m_ProcMap.erase(strApp) != 0; + WriteLog(strApp + _(" terminated.")); + return m_hmProcMap.erase(strApp) != 0; } void TLurchServer::StopProcess(const wxString& strApp) @@ -397,14 +394,25 @@ void TLurchServer::StopProcess(const wxString& strApp) wxSocketClient sock; if (sock.Connect(addr)) { + // Prima provo con le buone inviando una richiesta di stop const wxString str = "GET /stop.cgi HTTP/1.1\r\n\r\n"; sock.Write(str, str.Length()); - if (strHost == "localhost") + if (addr.IsLocalHost()) { const wxSingleInstanceChecker sic(strApp); - for (int i = 0; i < 5 && sic.IsAnotherRunning(); i++) + int i; + for (i = 5; i >= 0 && sic.IsAnotherRunning(); i--) wxSleep(1); - ForgetProcess(strApp); + // Se le buone non hanno funzionato ... passo alle cattive + if (i < 0) + { + wxProcess* p = m_hmProcMap[strApp]; + if (p != NULL) + { + p->Kill(p->GetPid()); + ForgetProcess(strApp); + } + } } } } @@ -412,24 +420,46 @@ void TLurchServer::StopProcess(const wxString& strApp) void TLurchServer::ProcessFormStop(const THashTable& args, wxSocketBase& sock) { - const wxString strApp = args.Get("App"); - if (strApp == GetAppName()) // Stop myself! - { - wxArrayString app; CreateServersList(app); - for (size_t i = 0; i < app.GetCount(); i++) - { - if (app[i] != GetAppName()) // Stop Children only! - { - const wxSingleInstanceChecker sic(app[i]); - if (sic.IsAnotherRunning()) - StopProcess(app[i]); - } - } - } - StopProcess(strApp); - MessageBox("Server stopped", strApp, sock); + if (m_Semaphore.Wait() == wxSEMA_NO_ERROR) + { + const wxString strApp = args.Get("App"); + if (strApp == GetAppName() || strApp.IsEmpty()) // Stop myself! + { + m_PingTimer.Stop(); // Stop ping process! + // Fill a safe array of names before killing the processes stored in the hash map + const TProcessHashMap& hmServers = GetRunningServers(); // Hash map of the running processes + wxArrayString aRunner; // Array of the names of the runners + TProcessHashMap::const_iterator it; + for (it = hmServers.begin(); it != hmServers.end(); ++it) + aRunner.Add(it->first); + for (size_t i = 0; i < aRunner.GetCount(); i++) + StopProcess(aRunner[i]); + ExitMainLoop(); + } + else + StopProcess(strApp); + MessageBox("Server stopped", strApp, sock); + + m_Semaphore.Post(); + } } +void TLurchServer::ProcessFormRestart(const THashTable& args, wxSocketBase& sock) +{ + const wxString strApp = args.Get("App"); + if (!strApp.IsEmpty() && strApp != GetAppName()) + { + if (m_Semaphore.Wait() == wxSEMA_NO_ERROR) + { + StopProcess(strApp); + ::wxSleep(2); + ProcessFormStart(args, sock); + m_Semaphore.Post(); + } + } +} + + void TLurchServer::EnumerateVariables(const wxString& strApp, wxArrayString& arr) const { wxFileInputStream inf(GetConfigName()); @@ -542,10 +572,13 @@ void TLurchServer::CallCgi(wxString& strFileName, wxSocketBase& sock) if (strExt == "cgi") { + strName.MakeLower(); if (strName == "start") ProcessFormStart(hashArgs, sock); else if (strName == "kill") ProcessFormStop(hashArgs, sock); else + if (strName == "restart") + ProcessFormRestart(hashArgs, sock); else if (strName == "config") ProcessFormConfig(hashArgs, sock); else if (strName == "update") @@ -593,23 +626,32 @@ void TLurchServer::ProcessHttpPost(wxString cmd, wxSocketBase& outs) bool TLurchServer::Initialization() { - m_nFreq = GetConfigInt("PingFreq") * 1000; // sec to msec - - wxArrayString arr; CreateServersList(arr); + const wxArrayString& arr = GetServersList(); for (size_t i = 0; i < arr.GetCount(); i++) { const wxString& strApp = arr[i]; - const bool bAutorun = GetConfigBool("Autorun", false, strApp); - if (bAutorun) - StartProcess(strApp); + if (strApp != GetAppName()) + { + const bool bAutorun = GetConfigBool("Autorun", false, strApp); + if (bAutorun) + StartProcess(strApp); + } } - RestartTimer(); - - + const int nFreq = GetConfigInt("PingFreq"); + if (nFreq > 0) + { + m_PingTimer.Start(nFreq * 1000); // sec to msec + m_Semaphore.Post(); // GREEN! + } + return true; } // Istanziare l'applicazione principale IMPLEMENT_APP(TLurchServer) + +TLurchServer::TLurchServer() + : m_Semaphore(0 /* RED! */, 1 /* mutex like behaviour */) +{ } diff --git a/server/xml.cpp b/server/xml.cpp index 2ef62e7f5..c630b2fa0 100755 --- a/server/xml.cpp +++ b/server/xml.cpp @@ -1,4 +1,5 @@ #include + #include "wx/mstream.h" #include