#include "FilesStatisticsService.h" #include //for getting disk free space #include //for getting files sizes #include "Time.h" // -------------------------------------------------------------------------- // //! Default constructor. Should NOT be used otherwise the service name will be wrong // //FilesStatisticsService::FilesStatisticsService() //{ // FilesStatisticsService("YouDidntCallTheProperConstructor"); //} // -------------------------------------------------------------------------- // //! Constructor with correct service name. The state machine using this object should give it //! its own name as a parameter //! @param serverName the name of the server which created this object // FilesStatisticsService::FilesStatisticsService(const string& serverName, MessageImp* mess) : fServerName(serverName), fCurrentFolder("."), fContinueStats(true), fDebug(false), fBaseSize(0), fPeriodDuration(1), fMess(mess) { fStats.sizeWritten = 0; fStats.freeSpace = 0; fStats.writingRate = 0; fService = new DimDescribedService(serverName + "/STATS", "X:3", fStats, "Statistics about size written"); fThread = boost::thread(boost::bind(&FilesStatisticsService::UpdateService, this)); } // -------------------------------------------------------------------------- // //! Default destructor. // FilesStatisticsService::~FilesStatisticsService() { fContinueStats = false; //WARNING: DO NOT DO JOIN OTHERWISE THE DESTRUCTOR BLOCKS // fThread.join(); delete fService; } // -------------------------------------------------------------------------- // //! Retrieves the free space of the current base path //! @return the available free space on disk, in bytes // long FilesStatisticsService::GetFreeSpace() { struct statvfs vfs; if (statvfs(fCurrentFolder.c_str(), &vfs)) return -1; return vfs.f_bsize*vfs.f_bavail; } // -------------------------------------------------------------------------- // //! Retrieves the size on disk of a given file, in bytes //! @param file the filename for which the size should be retrieved //! @return the size of the file, in bytes // long FilesStatisticsService::GetFileSizeOnDisk(const string& file) { errno = 0; struct stat st; if (!stat(file.c_str(), &st)) return st.st_size; if (errno != 0 && errno != 2)//ignoring error #2: no such file or directory is not an error for new files { ostringstream str; str << "Unable to stat " << file << ". Reason: " << strerror(errno) << " [" << errno << "]"; fMess->Error(str); } return -1; } // -------------------------------------------------------------------------- // //! Sets the current folder //! @param folder the path to the folder // bool FilesStatisticsService::SetCurrentFolder(const string& folder) { fMutex.lock(); fCurrentFolder = folder; fStats.freeSpace = GetFreeSpace(); if (fStats.freeSpace == -1) { fMess->Error("Could not stat the given folder. Ignoring it"); fMutex.unlock(); return false; } fMutex.unlock(); return true; } // -------------------------------------------------------------------------- // //! Updates the service. This is the function executed by the thread // void FilesStatisticsService::UpdateService() { Time cTime = Time(); Time previousTime; fStats.freeSpace = GetFreeSpace(); fService->updateService(); sleep(1); while (fContinueStats) { if (fPeriodDuration == 0.0f) { sleep(0.1f); continue; } sleep(fPeriodDuration); fMutex.lock(); previousTime = cTime; cTime = Time(); fStats.freeSpace = GetFreeSpace(); //gather the size of opened files. long previousSize = fStats.sizeWritten; fStats.sizeWritten = 0; for (set::const_iterator it = fOpenedFiles.begin(); it != fOpenedFiles.end(); it++) fStats.sizeWritten += GetFileSizeOnDisk(*it); fStats.sizeWritten -= fBaseSize; long timeElapsed = (cTime - previousTime).total_seconds(); if (timeElapsed != 0) fStats.writingRate = (fStats.sizeWritten - previousSize)/timeElapsed; else fStats.writingRate = 0; fService->updateService(); fMutex.unlock(); if (fDebug) { ostringstream str; str << "Size written: " << fStats.sizeWritten/1024 << " kB; writing rate: "; str << fStats.writingRate/1024 << " kB/s; free space: "; str << fStats.freeSpace/(1024*1024) << " MB"; fMess->Debug(str); } } } // -------------------------------------------------------------------------- // //! Let the object know that a new file has been opened //! @param fileName the full name of the file newly opened //! @return whether this file could be stated or not // bool FilesStatisticsService::FileOpened(const string& fileName) { if (fOpenedFiles.find(fileName) != fOpenedFiles.end()) return false; fMutex.lock(); //Add a newly opened file, and remember its original size long newSize = GetFileSizeOnDisk(fileName); if (newSize != -1) { fBaseSize += newSize; fOpenedFiles.insert(fileName); } else { fMess->Error("Could not stat file name: " + fileName); fMutex.unlock(); return false; } fMutex.unlock(); return true; } // -------------------------------------------------------------------------- // //! Set the debug mode on and off //! @param debug the new mode (true or false) // void FilesStatisticsService::SetDebugMode(bool debug) { if (fDebug == debug) { fMess->Error("Debug mode already in the asked state"); return; } fDebug = debug; if (fDebug) { fMess->Debug("Debug mode is now on"); } } // -------------------------------------------------------------------------- // //! Set the update of the service interval //! @param duration the duration between two services update, in second // void FilesStatisticsService::SetStatPeriod(const float duration) { if (duration < 0) { fMess->Error("Statistics period duration should be greater than zero. Discarding"); return; } if (!finite(duration)) { fMess->Error("Provided duration does not appear to be a valid float. Discarding"); return; } if (duration == fPeriodDuration) fMess->Warn("Statistics period not modified. Supplied value already in use"); if (duration == 0) fMess->Message("Statistics are now OFF"); else { ostringstream str; str << "Statistics period is now " << duration << " seconds"; fMess->Message(str); } fPeriodDuration = duration; } // -------------------------------------------------------------------------- // //! Retrieves the latest calculated size written and free disk space //! @param the FileStatisticsData data structure to be filled in // void FilesStatisticsService::GetTotalSizeWritten(FileStatisticsData& data) { data.sizeWritten = fStats.sizeWritten; data.writingRate = fStats.writingRate; data.freeSpace = fStats.freeSpace; } // -------------------------------------------------------------------------- // //! Resets the files statistics object. Basically sets all counters to zero // void FilesStatisticsService::Reset() { fStats.sizeWritten = 0; fStats.writingRate = 0; fBaseSize = 0; fOpenedFiles.clear(); }