mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-14 03:58:18 +00:00
Merge #1573
1573: Handle FS failures while updating (fixes #1560) r=heinrich5991 a=def- Not sure if I caught all locations Co-authored-by: def <dennis@felsin9.de>
This commit is contained in:
commit
52c00dcc02
|
@ -83,17 +83,25 @@ CRequest::CRequest(const char *pUrl, bool CanTimeout) :
|
||||||
|
|
||||||
void CRequest::Run()
|
void CRequest::Run()
|
||||||
{
|
{
|
||||||
if(BeforeInit())
|
if(!BeforeInit())
|
||||||
{
|
{
|
||||||
m_State = HTTP_ERROR;
|
m_State = HTTP_ERROR;
|
||||||
|
OnCompletion();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CURL *pHandle = curl_easy_init();
|
CURL *pHandle = curl_easy_init();
|
||||||
|
m_State = RunImpl(pHandle);
|
||||||
|
curl_easy_cleanup(pHandle);
|
||||||
|
|
||||||
|
OnCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CRequest::RunImpl(CURL *pHandle)
|
||||||
|
{
|
||||||
if(!pHandle)
|
if(!pHandle)
|
||||||
{
|
{
|
||||||
m_State = HTTP_ERROR;
|
return HTTP_ERROR;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(g_Config.m_DbgCurl)
|
if(g_Config.m_DbgCurl)
|
||||||
|
@ -142,31 +150,28 @@ void CRequest::Run()
|
||||||
curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
|
curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
|
||||||
curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
|
curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
|
||||||
|
|
||||||
if(AfterInit(pHandle))
|
if(!AfterInit(pHandle))
|
||||||
{
|
{
|
||||||
curl_easy_cleanup(pHandle);
|
return HTTP_ERROR;
|
||||||
m_State = HTTP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg_msg("http", "http %s", m_aUrl);
|
dbg_msg("http", "http %s", m_aUrl);
|
||||||
m_State = HTTP_RUNNING;
|
m_State = HTTP_RUNNING;
|
||||||
int Ret = curl_easy_perform(pHandle);
|
int Ret = curl_easy_perform(pHandle);
|
||||||
BeforeCompletion();
|
if(!BeforeCompletion())
|
||||||
|
{
|
||||||
|
return HTTP_ERROR;
|
||||||
|
}
|
||||||
if(Ret != CURLE_OK)
|
if(Ret != CURLE_OK)
|
||||||
{
|
{
|
||||||
dbg_msg("http", "task failed. libcurl error: %s", aErr);
|
dbg_msg("http", "task failed. libcurl error: %s", aErr);
|
||||||
m_State = (Ret == CURLE_ABORTED_BY_CALLBACK) ? HTTP_ABORTED : HTTP_ERROR;
|
return (Ret == CURLE_ABORTED_BY_CALLBACK) ? HTTP_ABORTED : HTTP_ERROR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dbg_msg("http", "task done %s", m_aUrl);
|
dbg_msg("http", "task done %s", m_aUrl);
|
||||||
m_State = HTTP_DONE;
|
return HTTP_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_cleanup(pHandle);
|
|
||||||
|
|
||||||
OnCompletion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CRequest::WriteCallback(char *pData, size_t Size, size_t Number, void *pUser)
|
size_t CRequest::WriteCallback(char *pData, size_t Size, size_t Number, void *pUser)
|
||||||
|
@ -265,7 +270,7 @@ CGetFile::CGetFile(IStorage *pStorage, const char *pUrl, const char *pDest, int
|
||||||
str_copy(m_aDest, pDest, sizeof(m_aDest));
|
str_copy(m_aDest, pDest, sizeof(m_aDest));
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGetFile::BeforeInit()
|
bool CGetFile::BeforeInit()
|
||||||
{
|
{
|
||||||
char aPath[512];
|
char aPath[512];
|
||||||
if(m_StorageType == -2)
|
if(m_StorageType == -2)
|
||||||
|
@ -274,15 +279,18 @@ int CGetFile::BeforeInit()
|
||||||
m_pStorage->GetCompletePath(m_StorageType, m_aDest, aPath, sizeof(aPath));
|
m_pStorage->GetCompletePath(m_StorageType, m_aDest, aPath, sizeof(aPath));
|
||||||
|
|
||||||
if(fs_makedir_rec_for(aPath) < 0)
|
if(fs_makedir_rec_for(aPath) < 0)
|
||||||
|
{
|
||||||
dbg_msg("http", "i/o error, cannot create folder for: %s", aPath);
|
dbg_msg("http", "i/o error, cannot create folder for: %s", aPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_File = io_open(aPath, IOFLAG_WRITE);
|
m_File = io_open(aPath, IOFLAG_WRITE);
|
||||||
if(!m_File)
|
if(!m_File)
|
||||||
{
|
{
|
||||||
dbg_msg("http", "i/o error, cannot open file: %s", m_aDest);
|
dbg_msg("http", "i/o error, cannot open file: %s", m_aDest);
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CGetFile::OnData(char *pData, size_t DataSize)
|
size_t CGetFile::OnData(char *pData, size_t DataSize)
|
||||||
|
@ -290,9 +298,9 @@ size_t CGetFile::OnData(char *pData, size_t DataSize)
|
||||||
return io_write(m_File, pData, DataSize);
|
return io_write(m_File, pData, DataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGetFile::BeforeCompletion()
|
bool CGetFile::BeforeCompletion()
|
||||||
{
|
{
|
||||||
io_close(m_File);
|
return io_close(m_File) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPostJson::CPostJson(const char *pUrl, bool CanTimeout, const char *pJson)
|
CPostJson::CPostJson(const char *pUrl, bool CanTimeout, const char *pJson)
|
||||||
|
@ -301,7 +309,7 @@ CPostJson::CPostJson(const char *pUrl, bool CanTimeout, const char *pJson)
|
||||||
str_copy(m_aJson, pJson, sizeof(m_aJson));
|
str_copy(m_aJson, pJson, sizeof(m_aJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPostJson::AfterInit(void *pCurl)
|
bool CPostJson::AfterInit(void *pCurl)
|
||||||
{
|
{
|
||||||
CURL *pHandle = (CURL *)pCurl;
|
CURL *pHandle = (CURL *)pCurl;
|
||||||
|
|
||||||
|
@ -310,5 +318,5 @@ int CPostJson::AfterInit(void *pCurl)
|
||||||
curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, pHeaders);
|
curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, pHeaders);
|
||||||
curl_easy_setopt(pHandle, CURLOPT_POSTFIELDS, m_aJson);
|
curl_easy_setopt(pHandle, CURLOPT_POSTFIELDS, m_aJson);
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <engine/kernel.h>
|
#include <engine/kernel.h>
|
||||||
|
|
||||||
typedef struct _json_value json_value;
|
typedef struct _json_value json_value;
|
||||||
|
typedef void CURL;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -21,12 +22,12 @@ class CRequest : public IJob
|
||||||
// Abort the request with an error if `BeforeInit()` or `AfterInit()`
|
// Abort the request with an error if `BeforeInit()` or `AfterInit()`
|
||||||
// returns something nonzero. Also abort the request if `OnData()`
|
// returns something nonzero. Also abort the request if `OnData()`
|
||||||
// returns something other than `DataSize`.
|
// returns something other than `DataSize`.
|
||||||
virtual int BeforeInit() { return 0; }
|
virtual bool BeforeInit() { return true; }
|
||||||
virtual int AfterInit(void *pCurl) { return 0; }
|
virtual bool AfterInit(void *pCurl) { return true; }
|
||||||
virtual size_t OnData(char *pData, size_t DataSize) = 0;
|
virtual size_t OnData(char *pData, size_t DataSize) = 0;
|
||||||
|
|
||||||
virtual void OnProgress() { }
|
virtual void OnProgress() { }
|
||||||
virtual void BeforeCompletion() { }
|
virtual bool BeforeCompletion() { return true; }
|
||||||
virtual void OnCompletion() { }
|
virtual void OnCompletion() { }
|
||||||
|
|
||||||
char m_aUrl[256];
|
char m_aUrl[256];
|
||||||
|
@ -43,6 +44,7 @@ class CRequest : public IJob
|
||||||
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
|
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
|
||||||
|
|
||||||
void Run();
|
void Run();
|
||||||
|
int RunImpl(CURL *pHandle);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CRequest(const char *pUrl, bool CanTimeout);
|
CRequest(const char *pUrl, bool CanTimeout);
|
||||||
|
@ -75,8 +77,8 @@ public:
|
||||||
class CGetFile : public CRequest
|
class CGetFile : public CRequest
|
||||||
{
|
{
|
||||||
virtual size_t OnData(char *pData, size_t DataSize);
|
virtual size_t OnData(char *pData, size_t DataSize);
|
||||||
virtual int BeforeInit();
|
virtual bool BeforeInit();
|
||||||
virtual void BeforeCompletion();
|
virtual bool BeforeCompletion();
|
||||||
|
|
||||||
IStorage *m_pStorage;
|
IStorage *m_pStorage;
|
||||||
|
|
||||||
|
@ -94,7 +96,7 @@ public:
|
||||||
class CPostJson : public CRequest
|
class CPostJson : public CRequest
|
||||||
{
|
{
|
||||||
virtual size_t OnData(char *pData, size_t DataSize) { return DataSize; }
|
virtual size_t OnData(char *pData, size_t DataSize) { return DataSize; }
|
||||||
virtual int AfterInit(void *pCurl);
|
virtual bool AfterInit(void *pCurl);
|
||||||
|
|
||||||
char m_aJson[1024];
|
char m_aJson[1024];
|
||||||
|
|
||||||
|
|
|
@ -135,23 +135,26 @@ void CUpdater::FetchFile(const char *pFile, const char *pDestPath)
|
||||||
m_pEngine->AddJob(std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath));
|
m_pEngine->AddJob(std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::MoveFile(const char *pFile)
|
bool CUpdater::MoveFile(const char *pFile)
|
||||||
{
|
{
|
||||||
char aBuf[256];
|
char aBuf[256];
|
||||||
size_t len = str_length(pFile);
|
size_t len = str_length(pFile);
|
||||||
|
bool Success = true;
|
||||||
|
|
||||||
if(!str_comp_nocase(pFile + len - 4, ".dll") || !str_comp_nocase(pFile + len - 4, ".ttf"))
|
if(!str_comp_nocase(pFile + len - 4, ".dll") || !str_comp_nocase(pFile + len - 4, ".ttf"))
|
||||||
{
|
{
|
||||||
str_format(aBuf, sizeof(aBuf), "%s.old", pFile);
|
str_format(aBuf, sizeof(aBuf), "%s.old", pFile);
|
||||||
m_pStorage->RenameBinaryFile(pFile, aBuf);
|
Success &= m_pStorage->RenameBinaryFile(pFile, aBuf);
|
||||||
str_format(aBuf, sizeof(aBuf), "update/%s", pFile);
|
str_format(aBuf, sizeof(aBuf), "update/%s", pFile);
|
||||||
m_pStorage->RenameBinaryFile(aBuf, pFile);
|
Success &= m_pStorage->RenameBinaryFile(aBuf, pFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
str_format(aBuf, sizeof(aBuf), "update/%s", pFile);
|
str_format(aBuf, sizeof(aBuf), "update/%s", pFile);
|
||||||
m_pStorage->RenameBinaryFile(aBuf, pFile);
|
Success &= m_pStorage->RenameBinaryFile(aBuf, pFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::Update()
|
void CUpdater::Update()
|
||||||
|
@ -174,43 +177,53 @@ void CUpdater::AddFileJob(const char *pFile, bool Job)
|
||||||
m_FileJobs[string(pFile)] = Job;
|
m_FileJobs[string(pFile)] = Job;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::ReplaceClient()
|
bool CUpdater::ReplaceClient()
|
||||||
{
|
{
|
||||||
dbg_msg("updater", "replacing " PLAT_CLIENT_EXEC);
|
dbg_msg("updater", "replacing " PLAT_CLIENT_EXEC);
|
||||||
|
bool Success = true;
|
||||||
|
|
||||||
// Replace running executable by renaming twice...
|
// Replace running executable by renaming twice...
|
||||||
if(!m_IsWinXP)
|
if(!m_IsWinXP)
|
||||||
{
|
{
|
||||||
m_pStorage->RemoveBinaryFile("DDNet.old");
|
m_pStorage->RemoveBinaryFile("DDNet.old");
|
||||||
m_pStorage->RenameBinaryFile(PLAT_CLIENT_EXEC, "DDNet.old");
|
Success &= m_pStorage->RenameBinaryFile(PLAT_CLIENT_EXEC, "DDNet.old");
|
||||||
m_pStorage->RenameBinaryFile("update/DDNet.tmp", PLAT_CLIENT_EXEC);
|
Success &= m_pStorage->RenameBinaryFile("update/DDNet.tmp", PLAT_CLIENT_EXEC);
|
||||||
}
|
}
|
||||||
#if !defined(CONF_FAMILY_WINDOWS)
|
#if !defined(CONF_FAMILY_WINDOWS)
|
||||||
char aPath[512];
|
char aPath[512];
|
||||||
m_pStorage->GetBinaryPath(PLAT_CLIENT_EXEC, aPath, sizeof aPath);
|
m_pStorage->GetBinaryPath(PLAT_CLIENT_EXEC, aPath, sizeof aPath);
|
||||||
char aBuf[512];
|
char aBuf[512];
|
||||||
str_format(aBuf, sizeof aBuf, "chmod +x %s", aPath);
|
str_format(aBuf, sizeof aBuf, "chmod +x %s", aPath);
|
||||||
if (system(aBuf))
|
if(system(aBuf))
|
||||||
|
{
|
||||||
dbg_msg("updater", "ERROR: failed to set client executable bit");
|
dbg_msg("updater", "ERROR: failed to set client executable bit");
|
||||||
|
Success &= false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::ReplaceServer()
|
bool CUpdater::ReplaceServer()
|
||||||
{
|
{
|
||||||
dbg_msg("updater", "replacing " PLAT_SERVER_EXEC);
|
dbg_msg("updater", "replacing " PLAT_SERVER_EXEC);
|
||||||
|
bool Success = true;
|
||||||
|
|
||||||
//Replace running executable by renaming twice...
|
//Replace running executable by renaming twice...
|
||||||
m_pStorage->RemoveBinaryFile("DDNet-Server.old");
|
m_pStorage->RemoveBinaryFile("DDNet-Server.old");
|
||||||
m_pStorage->RenameBinaryFile(PLAT_SERVER_EXEC, "DDNet-Server.old");
|
Success &= m_pStorage->RenameBinaryFile(PLAT_SERVER_EXEC, "DDNet-Server.old");
|
||||||
m_pStorage->RenameBinaryFile("update/DDNet-Server.tmp", PLAT_SERVER_EXEC);
|
Success &= m_pStorage->RenameBinaryFile("update/DDNet-Server.tmp", PLAT_SERVER_EXEC);
|
||||||
#if !defined(CONF_FAMILY_WINDOWS)
|
#if !defined(CONF_FAMILY_WINDOWS)
|
||||||
char aPath[512];
|
char aPath[512];
|
||||||
m_pStorage->GetBinaryPath(PLAT_SERVER_EXEC, aPath, sizeof aPath);
|
m_pStorage->GetBinaryPath(PLAT_SERVER_EXEC, aPath, sizeof aPath);
|
||||||
char aBuf[512];
|
char aBuf[512];
|
||||||
str_format(aBuf, sizeof aBuf, "chmod +x %s", aPath);
|
str_format(aBuf, sizeof aBuf, "chmod +x %s", aPath);
|
||||||
if (system(aBuf))
|
if (system(aBuf))
|
||||||
|
{
|
||||||
dbg_msg("updater", "ERROR: failed to set server executable bit");
|
dbg_msg("updater", "ERROR: failed to set server executable bit");
|
||||||
|
Success &= false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::ParseUpdate()
|
void CUpdater::ParseUpdate()
|
||||||
|
@ -323,15 +336,19 @@ void CUpdater::PerformUpdate()
|
||||||
|
|
||||||
void CUpdater::CommitUpdate()
|
void CUpdater::CommitUpdate()
|
||||||
{
|
{
|
||||||
|
bool Success = true;
|
||||||
|
|
||||||
for(map<std::string, bool>::iterator it = m_FileJobs.begin(); it != m_FileJobs.end(); ++it)
|
for(map<std::string, bool>::iterator it = m_FileJobs.begin(); it != m_FileJobs.end(); ++it)
|
||||||
if(it->second)
|
if(it->second)
|
||||||
MoveFile(it->first.c_str());
|
Success &= MoveFile(it->first.c_str());
|
||||||
|
|
||||||
if(m_ClientUpdate)
|
if(m_ClientUpdate)
|
||||||
ReplaceClient();
|
Success &= ReplaceClient();
|
||||||
if(m_ServerUpdate)
|
if(m_ServerUpdate)
|
||||||
ReplaceServer();
|
Success &= ReplaceServer();
|
||||||
if(m_pClient->State() == IClient::STATE_ONLINE || m_pClient->EditorHasUnsavedData())
|
if(!Success)
|
||||||
|
m_State = FAIL;
|
||||||
|
else if(m_pClient->State() == IClient::STATE_ONLINE || m_pClient->EditorHasUnsavedData())
|
||||||
m_State = NEED_RESTART;
|
m_State = NEED_RESTART;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,14 +56,14 @@ class CUpdater : public IUpdater
|
||||||
|
|
||||||
void AddFileJob(const char *pFile, bool Job);
|
void AddFileJob(const char *pFile, bool Job);
|
||||||
void FetchFile(const char *pFile, const char *pDestPath = 0);
|
void FetchFile(const char *pFile, const char *pDestPath = 0);
|
||||||
void MoveFile(const char *pFile);
|
bool MoveFile(const char *pFile);
|
||||||
|
|
||||||
void ParseUpdate();
|
void ParseUpdate();
|
||||||
void PerformUpdate();
|
void PerformUpdate();
|
||||||
void CommitUpdate();
|
void CommitUpdate();
|
||||||
|
|
||||||
void ReplaceClient();
|
bool ReplaceClient();
|
||||||
void ReplaceServer();
|
bool ReplaceServer();
|
||||||
|
|
||||||
void SetCurrentState(int NewState);
|
void SetCurrentState(int NewState);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue