source: trunk/FACT++/src/DimWriteStatistics.cc@ 15350

Last change on this file since 15350 was 13159, checked in by tbretz, 13 years ago
Assume that an empty folder name means '.'
File size: 8.7 KB
Line 
1//*************************************************************************************
2/** @class DimWriteStatistics
3
4 @brief provides a statistics service telling the free space on disk and the total size written so far
5
6*/
7//*************************************************************************************
8#include "DimWriteStatistics.h"
9
10#include <sys/statvfs.h> //for getting disk free space
11#include <sys/stat.h> //for getting files sizes
12
13#include <boost/filesystem.hpp>
14
15#include "Time.h"
16
17using namespace std;
18using namespace boost::posix_time;
19
20// --------------------------------------------------------------------------
21//
22//! Constructor with correct service name. The state machine using this object should give it
23//! its own name as a parameter
24//! @param serverName the name of the server which created this object
25//
26DimWriteStatistics::DimWriteStatistics(const string& server, MessageImp &log) :
27 fLog(log),
28 fDimService(server + "/STATS", "X:4", "Statistics about size written"),
29 fCurrentFolder("."),
30 fUpdateInterval(1000),
31 fBaseSize(0),
32 fDebug(false)
33{
34 fThread = boost::thread(boost::bind(&DimWriteStatistics::UpdateService, this));
35}
36
37// --------------------------------------------------------------------------
38//
39//! Destructor. Stop thread by setting fUpdateInterval to 0 and join the
40//! thread.
41//
42DimWriteStatistics::~DimWriteStatistics()
43{
44 fUpdateInterval = 0;
45
46 // This blocks for fPeriod duration, but maybe canceling the thread
47 // could be more dangerous leaving Dim in an undefined state.
48 fThread.interrupt();
49}
50
51int DimWriteStatistics::Write(const Time &t, const string &txt, int qos)
52{
53 return fLog.Write(t, txt, qos);
54}
55
56// --------------------------------------------------------------------------
57//
58//! Retrieves the free space of the current base path
59//! @return the available free space on disk, in bytes
60//
61int64_t DimWriteStatistics::GetFreeSpace()
62{
63 struct statvfs vfs;
64 if (statvfs(fCurrentFolder.c_str(), &vfs))
65 return -1;
66
67 return vfs.f_bsize*vfs.f_bavail;
68}
69
70// --------------------------------------------------------------------------
71//
72//! Retrieves the size on disk of a given file, in bytes
73//! @param file the filename for which the size should be retrieved
74//! @return the size of the file, in bytes
75//
76int64_t DimWriteStatistics::GetFileSizeOnDisk(const string& file, MessageImp &log)
77{
78 errno = 0;
79 struct stat st;
80 if (!stat(file.c_str(), &st))
81 return st.st_size;
82
83 //ignoring error #2: no such file or directory is not an error for new files
84 if (errno == 0 || errno == 2)
85 return 0;
86
87 ostringstream str;
88 str << "stat() failed for '" << file << "': " << strerror(errno) << " [errno=" << errno << "]";
89 log.Error(str);
90
91 return -1;
92}
93
94// --------------------------------------------------------------------------
95//
96//! Check if a given path exists
97//! @param path the path to be checked
98//! @return whether or not the given path exists
99//
100bool DimWriteStatistics::DoesPathExist(string path, MessageImp &log)
101{
102 namespace fs = boost::filesystem;
103
104 if (path.empty())
105 path = ".";
106
107 const fs::path fullPath = fs::system_complete(fs::path(path));
108
109 if (!fs::exists(fullPath))
110 return false;
111
112 if (!fs::is_directory(fullPath))
113 {
114 log.Error("Path given for checking '" + path + "' designate a file name. Please provide a path name only");
115 return false;
116 }
117
118 if (access(path.c_str(), R_OK|W_OK|X_OK) != 0)
119 {
120 log.Error("Missing read, write or execute permissions on directory '" + path + "'");
121 return false;
122 }
123
124 return true;
125}
126
127// --------------------------------------------------------------------------
128//
129//! Check if a given path exists
130//! @param path the path to be checked
131//! @return whether or not the creation has been successfull
132//
133void DimWriteStatistics::CreateDirectory(string path)
134{
135 namespace fs = boost::filesystem;
136
137 //remove last '/', if present
138 if (path[path.size()-1] == '/')
139 path = path.substr(0, path.size()-1);
140
141 //create boost path
142 const fs::path fullPath = fs::system_complete(fs::path(path));
143
144 //if path does not exist, check if upper levels exist already
145 if (fs::exists(fullPath))
146 {
147 //if path already exist, make sure it does not designate a file (filenames cannot be checked if they do not exist)
148 if (fs::is_directory(fullPath))
149 return;
150
151 throw runtime_error("Path to be created is a file '" + path + "'");
152 }
153
154 // we're hitting "/", which SHOULD have existed...
155 if (path.size() <= 1)
156 throw runtime_error("Path to be created looks suspicious '"+path+"'");
157
158 if (path.find_last_of('/')!=string::npos)
159 CreateDirectory(path.substr(0, path.find_last_of('/')));
160
161 //path does not exist, and upper level have been created already by recusrion.
162 const mode_t rightsMask = S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH; //everybody read, owner writes
163
164 if (mkdir(path.c_str(), rightsMask)==0)
165 return;
166
167 ostringstream str;
168 str << "mkdir() failed for '" << path << "': " << strerror(errno) << " [errno=" << errno << "]";
169 throw runtime_error(str.str());
170}
171
172// --------------------------------------------------------------------------
173//
174//! Sets the current folder
175//! @param folder the path to the folder
176//
177bool DimWriteStatistics::SetCurrentFolder(const string& folder)
178{
179 struct statvfs vfs;
180 if (statvfs(folder.empty()?".":folder.c_str(), &vfs))
181 {
182 fLog.Error("statvfs() failed for '"+folder+"'... ignoring it.");
183 return false;
184 }
185
186 fCurrentFolder = folder.empty()?".":folder;
187 return true;
188}
189
190// --------------------------------------------------------------------------
191//
192//! Updates the service. This is the function executed by the thread
193//
194void DimWriteStatistics::UpdateService()
195{
196 Time previousTime;
197 uint64_t previousSize = 0;
198
199 while (1)
200 {
201 if (fUpdateInterval==0)
202 {
203 boost::this_thread::interruption_point();
204 boost::this_thread::yield();
205 continue;
206 }
207
208 Stats data;
209
210 for (set<string>::const_iterator it = fOpenedFiles.begin(); it != fOpenedFiles.end(); it++)
211 data.sizeWritten += GetFileSizeOnDisk(*it);
212 data.sizeWritten -= fBaseSize;
213
214 const Time cTime = Time();
215
216 data.freeSpace = GetFreeSpace();
217 data.rateWritten = data.sizeWritten-previousSize;
218 data.timeElapsed = (cTime - previousTime).total_milliseconds();
219
220 previousSize = data.sizeWritten;
221 previousTime = cTime;
222
223 fDimService.Update(data);
224
225 fStats = data;
226
227 if (fDebug)
228 {
229 ostringstream str;
230 str << "Written: " << fStats.sizeWritten/1000 << " kB; writing rate: ";
231 str << fStats.rateWritten/fStats.timeElapsed << " kB/s; free space: ";
232 str << fStats.freeSpace/1000000 << " MB";
233 fLog.Debug(str);
234 }
235
236 boost::this_thread::sleep(milliseconds(fUpdateInterval));
237 }
238}
239// --------------------------------------------------------------------------
240//
241//! Let the object know that a new file has been opened
242//! @param fileName the full name of the file newly opened
243//! @return whether this file could be stated or not
244//
245bool DimWriteStatistics::FileOpened(const string& fileName)
246{
247 if (fOpenedFiles.find(fileName) != fOpenedFiles.end())
248 return false;
249
250 //Add a newly opened file, and remember its original size
251 const int64_t newSize = GetFileSizeOnDisk(fileName);
252 if (newSize == -1)
253 return false;
254
255 fBaseSize += newSize;
256 fOpenedFiles.insert(fileName);
257
258 return true;
259}
260// --------------------------------------------------------------------------
261//
262//! Set the debug mode on and off
263//! @param debug the new mode (true or false)
264//
265void DimWriteStatistics::SetDebugMode(bool debug)
266{
267 fDebug = debug;
268
269 if (fDebug)
270 fLog.Debug("Debug mode is now on.");
271}
272// --------------------------------------------------------------------------
273//
274//! Set the update of the service interval
275//! @param duration the duration between two services update, in second
276//
277void DimWriteStatistics::SetUpdateInterval(const int16_t duration)
278{
279 if (!finite(duration))
280 {
281 fLog.Error("Provided update interval is not a valid float... discarding.");
282 return;
283 }
284 if (uint16_t(duration) == fUpdateInterval)
285 {
286 fLog.Warn("Statistics update interval not modified. Supplied value already in use.");
287 return;
288 }
289
290 if (duration <= 0)
291 fLog.Message("Statistics are now OFF.");
292 else
293 {
294 ostringstream str;
295 str << "Statistics update interval is now " << duration << " seconds";
296 fLog.Message(str);
297 }
298
299 fUpdateInterval = duration<0 ? 0 : duration;
300}
Note: See TracBrowser for help on using the repository browser.