Added ai command setEquipment
[ryzomcore.git] / ryzom / server / src / backup_service / backup_file_access.cpp
blob12b45c7a594c6e4ff99159663b7691323bc35fbb
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "backup_file_access.h"
19 #include "nel/misc/path.h"
20 #include "nel/misc/mem_stream.h"
21 #include "nel/misc/file.h"
22 #include "nel/net/service.h"
24 #include "game_share/backup_service_messages.h"
25 #include "server_share/backup_service_itf.h"
27 #include "backup_service.h"
29 using namespace NLNET;
32 /* These variables are deprecated. Now the BS only deals with paths relative to SaveShardRoot.
33 NLMISC::CVariable<std::string> BSFilePrefix("backup", "BSFilePrefix", "file read/write prefix", "", 0, true);
34 NLMISC::CVariable<std::string> BSFileSubst("backup", "BSFileSubst", "file read/write substitute part", "", 0, true);
36 NLMISC::CVariable<bool> VerboseLog("backup", "VerboseLog", "Activate verbose logging of BS activity", false);
37 NLMISC::CVariable<bool> UseTempFile("backup", "UseTempFile", "Flag the use of temporary file for safe write or append operation", true, true);
39 extern NLMISC::CVariable<std::string> SaveShardRootGameShare;
41 bool bsstrincmp(const char* s1, const char* s2, int n)
43 int nn;
44 for (nn = (int)n; nn>0 && *s1 != '\0' && *s2 != '\0' && tolower(*s1) == tolower(*s2); --nn, ++s1, ++s2)
46 return nn == 0 || (*s1 == *s2);
49 std::string getBackupFileName(const std::string& filename)
51 return SaveShardRootGameShare.get() + filename;
52 /* // BSFilePrefix and BSFileSubst are deprecated
53 if (BSFilePrefix.get().empty())
54 return filename;
56 if (!BSFileSubst.get().empty() && bsstrincmp(BSFileSubst.get().c_str(), filename.c_str(), BSFileSubst.get().size()))
57 return BSFilePrefix.get() + filename.substr(BSFileSubst.get().size());
58 else
59 return BSFilePrefix.get() + filename;
64 // Init File manager
65 void CFileAccessManager::init()
67 _Mode = Normal;
68 _StallAllowed = true;
69 _Accesses.clear();
72 // Add an access to perform to stack of accesses
73 void CFileAccessManager::stackFileAccess(IFileAccess* access)
75 _Accesses.push_back(access);
78 // Flushes file accesses
79 void CFileAccessManager::update()
81 // while we are in normal mode, and there are still accesses to perform
82 while (_Mode == Normal && !_Accesses.empty())
84 // get next access
85 IFileAccess* access = _Accesses.front();
86 IFileAccess::TReturnCode rc = access->execute(*this);
88 if (rc == IFileAccess::MajorFailure)
90 nlwarning("Failed to execute access to file '%s', setting to STALLED mode", access->Filename.c_str());
91 setMode(Stalled, access->FailureReason);
93 else
95 if (rc == IFileAccess::MinorFailure)
97 nlwarning("Minor failure in access to file '%s', access is discarded yet.", access->Filename.c_str());
100 _Accesses.pop_front();
101 delete access;
106 // Release File manager
107 void CFileAccessManager::release()
109 if (!_Accesses.empty())
110 update();
112 if (!_Accesses.empty())
114 nlwarning("WARNING: release asked as file manager still contains pending accesses!");
115 nlwarning("DATA WILL BE LOST!!");
121 // Force manager mode
122 void CFileAccessManager::setMode(TMode mode, const std::string& reason)
124 if (mode == _Mode)
125 return;
127 _Mode = mode;
128 _Reason = reason;
130 NLNET::CMessage msgout("STALL_MODE");
131 bool stalled = (_Mode != Normal);
132 std::string reasonstr = _Reason;
134 msgout.serial(stalled, reasonstr);
135 NLNET::CUnifiedNetwork::getInstance()->send("EGS", msgout);
137 if (_Mode == Stalled)
138 IService::getInstance()->setCurrentStatus("Stalled");
139 else
140 IService::getInstance()->clearCurrentStatus("Stalled");
146 // Notify service connection
147 void CFileAccessManager::notifyServiceConnection(NLNET::TServiceId serviceId, const std::string& serviceName)
149 if (serviceName == "EGS" && _Mode == Stalled)
151 NLNET::CMessage msgout("STALL_MODE");
152 bool stalled = true;
153 std::string reason = _Reason;
155 msgout.serial(stalled, reason);
156 NLNET::CUnifiedNetwork::getInstance()->send(serviceId, msgout);
162 // display stacked accesses
163 void CFileAccessManager::displayFileAccesses(NLMISC::CLog& log)
165 uint i;
166 for (i=0; i<_Accesses.size(); ++i)
168 log.displayRawNL("%2d %08p %s %s %s", i, _Accesses[i], _Accesses[i]->Requester.toString().c_str(), _Accesses[i]->Filename.c_str(), _Accesses[i]->FailureReason.c_str());
173 // Remove a file access
174 void CFileAccessManager::removeFileAccess(IFileAccess* access)
176 std::deque<IFileAccess*>::iterator it;
177 for (it=_Accesses.begin(); it!=_Accesses.end(); ++it)
179 if ((*it) == access)
181 delete (*it);
182 _Accesses.erase(it);
183 return;
190 IFileAccess::TReturnCode CLoadFile::execute(CFileAccessManager& manager)
192 bool fileExists = NLMISC::CFile::fileExists(getBackupFileName(Filename));
194 if (!fileExists && checkFailureMode(MajorFailureIfFileNotExists))
196 FailureReason = NLMISC::toString("MAJOR_FAILURE:LOAD: file '%s' doesn't not exist", Filename.c_str());
197 return MajorFailure;
200 CBackupMsgReceiveFile outMsg;
201 outMsg.RequestId = RequestId;
203 NLMISC::CIFile f;
204 bool fileOpen = (fileExists && f.open(getBackupFileName(Filename)));
205 bool fileRead = false;
207 if (fileOpen)
209 outMsg.FileDescription.set(getBackupFileName(Filename));
211 // restore filename with the provided one for the response
212 outMsg.FileDescription.FileName = Filename;
216 H_AUTO(LoadFileSerial);
217 outMsg.Data.invert();
218 f.serialBuffer( outMsg.Data.bufferToFill(outMsg.FileDescription.FileSize), outMsg.FileDescription.FileSize);
219 outMsg.Data.invert();
220 fileRead = true;
221 f.close();
223 catch(const NLMISC::Exception &)
228 if (!fileRead && checkFailureMode(MajorFailureIfFileUnreaddable))
230 FailureReason = NLMISC::toString("MAJOR_FAILURE:LOAD: can't %s file '%s'", (!fileOpen ? "open" : "read"), Filename.c_str());
231 return MajorFailure;
234 // outMsg.send(Requester);
235 switch (Requester.RequesterType)
237 case TRequester::rt_service:
239 H_AUTO(CBackupMsgReceiveFile1);
240 NLNET::CMessage msgOut("bs_file");
241 outMsg.serial(msgOut);
242 NLNET::CUnifiedNetwork::getInstance()->send( Requester.ServiceId, msgOut );
244 break;
245 case TRequester::rt_layer3:
247 NLNET::CMessage msgOut("bs_file");
248 outMsg.serial(msgOut);
249 Requester.Netbase->send(msgOut, Requester.From);
251 break;
252 case TRequester::rt_module:
254 BS::CBackupServiceClientProxy bsc(Requester.ModuleProxy);
256 bsc.loadFileResult(CBackupService::getInstance()->getBackupModule(),
257 RequestId,
258 outMsg.FileDescription.FileName,
259 outMsg.FileDescription.FileTimeStamp,
260 NLNET::TBinBuffer(outMsg.Data.buffer(), outMsg.Data.length()));
264 // If the file read failed for any other reason than file not found then complain
265 if (!fileRead && fileExists)
267 FailureReason = NLMISC::toString("MINOR_FAILURE:LOAD: can't %s file '%s'", (!fileOpen ? "open" : "read"), Filename.c_str());
268 return MinorFailure;
271 if (VerboseLog)
273 // We can assume that this is the only case where the file read failed that hasn't already been treated above
274 if (!fileExists)
276 nldebug("Load file Failed (but MajorFailureIfFileUnreaddable==false): file '%s' doesn't not exist", Filename.c_str());
278 else
280 nlinfo("Loaded file '%s'", Filename.c_str());
284 return Success;
289 CWriteFile::CWriteFile(const std::string& filename, const TRequester &requester, uint32 requestid, NLMISC::CMemStream& data)
290 : IFileAccess(filename, requester, requestid)
292 BackupFile = false;
293 Append = false;
294 CreateDir = true;
295 FailureMode = MajorFailureMode;
296 uint32 startPos = (uint32)data.getPos();
297 uint32 actualLen = data.length()-startPos;
298 Data.resize(actualLen);
299 memcpy(&(Data[0]), data.buffer()+startPos, actualLen);
302 CWriteFile::CWriteFile(const std::string& filename, const TRequester &requester, uint32 requestid, uint8* data, uint dataSize)
303 : IFileAccess(filename, requester, requestid)
305 BackupFile = false;
306 Append = false;
307 CreateDir = true;
308 FailureMode = MajorFailureMode;
309 Data.resize(dataSize);
310 memcpy(&(Data[0]), data, dataSize);
313 IFileAccess::TReturnCode CWriteFile::execute(CFileAccessManager& manager)
315 bool fileExists = NLMISC::CFile::fileExists(getBackupFileName(Filename));
317 if (!fileExists)
319 if (checkFailureMode(MajorFailureIfFileNotExists))
321 FailureReason = NLMISC::toString("MAJOR_FAILURE:WRITE: file '%s' does not exist", Filename.c_str());
322 return MajorFailure;
324 if (checkFailureMode(MinorFailureIfFileNotExists))
326 FailureReason = NLMISC::toString("MINOR_FAILURE:WRITE: file '%s' does not exist", Filename.c_str());
327 return MinorFailure;
330 else
332 if (checkFailureMode(MajorFailureIfFileExists))
334 FailureReason = NLMISC::toString("MAJOR_FAILURE:WRITE: file '%s' already exists", Filename.c_str());
335 return MajorFailure;
337 if (checkFailureMode(MinorFailureIfFileExists))
339 FailureReason = NLMISC::toString("MINOR_FAILURE:WRITE: file '%s' already exists", Filename.c_str());
340 return MinorFailure;
344 if (CreateDir)
346 std::string dir = NLMISC::CFile::getPath(getBackupFileName(Filename));
348 if (!NLMISC::CFile::isExists(dir))
350 if (!NLMISC::CFile::createDirectoryTree(dir) ||
351 !NLMISC::CFile::setRWAccess(dir))
353 if (checkFailureMode(MajorFailureIfFileUnwritable))
355 FailureReason = NLMISC::toString("MAJOR_FAILURE:WRITE: can't open file '%s', failed to create directory", Filename.c_str());
356 return MajorFailure;
358 FailureReason = NLMISC::toString("MINOR_FAILURE:WRITE: can't open file '%s', failed to create directory", Filename.c_str());
359 return MinorFailure;
362 if (VerboseLog)
363 nlinfo("Created directory tree '%s'", dir.c_str());
367 // if can't open file, file is unwritable, failure in all cases (and no backup)
368 // file is kept untouched
369 NLMISC::COFile f;
370 bool fileOpen = f.open(getBackupFileName(Filename), Append, false, UseTempFile);
371 if (!fileOpen)
373 if (checkFailureMode(MajorFailureIfFileUnwritable))
375 FailureReason = NLMISC::toString("MAJOR_FAILURE:WRITE: can't open file '%s'", Filename.c_str());
376 return MajorFailure;
378 FailureReason = NLMISC::toString("MINOR_FAILURE:WRITE: can't open file '%s'", Filename.c_str());
379 return MinorFailure;
382 bool fileSaved = false;
386 f.serialBuffer(&(Data[0]), (uint)Data.size());
387 fileSaved = true;
389 if (VerboseLog)
390 nlinfo("%s %u octets to file '%s'", Append ? "Append" : "Save", Data.size(), Filename.c_str());
392 catch(const NLMISC::Exception &)
396 // if can't write in file, file is unwritable, failure in all cases (and no backup)
397 // file is kept untouched
398 if (!fileSaved && checkFailureMode(MajorFailureIfFileUnwritable))
400 if (checkFailureMode(MajorFailureIfFileUnwritable))
402 FailureReason = NLMISC::toString("MAJOR_FAILURE:WRITE: can't write file '%s'", Filename.c_str());
403 return MajorFailure;
405 FailureReason = NLMISC::toString("MINOR_FAILURE:WRITE: can't write file '%s'", Filename.c_str());
406 return MinorFailure;
409 // if backup failed
410 // -> if it is a major issue, don't write file
411 // -> else write file anyway
412 bool fileBackuped = true;
413 if (fileExists && BackupFile)
415 if (!NLMISC::CFile::copyFile( getBackupFileName(Filename)+".backup", getBackupFileName(Filename)))
417 fileBackuped = false;
418 if (checkFailureMode(MajorFailureIfFileUnbackupable))
420 FailureReason = NLMISC::toString("MAJOR_FAILURE:WRITE: can't backup file '%s'", Filename.c_str());
421 return MajorFailure;
426 f.close();
428 if (!fileBackuped)
430 FailureReason = NLMISC::toString("MINOR_FAILURE:WRITE: can't backup file '%s'", Filename.c_str());
431 return MinorFailure;
434 NLMISC::COFile flog;
435 std::string str = getBackupFileName(Filename)+"\n";
436 if(str.find("characters")!=std::string::npos)
438 flog.open(getBackupFileName("new_save.txt"), true);
439 flog.serialBuffer((uint8*)&(str[0]), str.size());
440 flog.close();
443 return Success;
447 IFileAccess::TReturnCode CDeleteFile::execute(CFileAccessManager& manager)
449 bool fileExists = NLMISC::CFile::fileExists(getBackupFileName(Filename));
451 if (!fileExists)
453 if (checkFailureMode(MajorFailureIfFileNotExists))
455 FailureReason = NLMISC::toString("MAJOR_FAILURE:DELETE: file '%s' does not exist", Filename.c_str());
456 return MajorFailure;
458 if (checkFailureMode(MinorFailureIfFileNotExists))
460 FailureReason = NLMISC::toString("MINOR_FAILURE:DELETE: file '%s' does not exist", Filename.c_str());
461 return MinorFailure;
463 return Success;
466 if (BackupFile)
468 bool fileBackuped = false;
471 std::string path = NLMISC::CFile::getPath(getBackupFileName(Filename));
472 std::string file = NLMISC::CFile::getFilename(Filename);
473 std::string backup;
474 uint i = 0;
477 backup = NLMISC::toString("%sdelete.%04u.%s", path.c_str(), i++, file.c_str());
479 while (i <= 10000 && NLMISC::CFile::fileExists(backup));
481 fileBackuped = (i <= 10000 && NLMISC::CFile::moveFile(backup, getBackupFileName(Filename)));
483 catch (...)
487 if (!fileBackuped)
489 if (checkFailureMode(MajorFailureIfFileUnbackupable))
491 FailureReason = NLMISC::toString("MAJOR_FAILURE:DELETE: can't backup file '%s'", Filename.c_str());
492 return MajorFailure;
494 FailureReason = NLMISC::toString("MINOR_FAILURE:DELETE: can't backup file '%s'", Filename.c_str());
495 return MinorFailure;
498 else
500 if (!NLMISC::CFile::deleteFile(getBackupFileName(Filename)))
502 if (checkFailureMode(MajorFailureIfFileUnDeletable))
504 FailureReason = NLMISC::toString("MAJOR_FAILURE:DELETE: can't delete file '%s'", Filename.c_str());
505 return MajorFailure;
507 FailureReason = NLMISC::toString("MINOR_FAILURE:DELETE: can't delete file '%s'", Filename.c_str());
508 return MinorFailure;
512 if (VerboseLog)
513 nlinfo("Deleted file '%s'", Filename.c_str());
515 return Success;