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

Last change on this file since 12105 was 11974, checked in by tbretz, 13 years ago
Fixed the case in which DoesPathExist gets no path at all and the case in which create directory gets a relative path.
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 if (GetFreeSpace() == -1)
180 {
181 fLog.Error("statvfs() failed for '"+folder+"'... ignoring it.");
182 return false;
183 }
184
185 fCurrentFolder = folder;
186 return true;
187}
188
189// --------------------------------------------------------------------------
190//
191//! Updates the service. This is the function executed by the thread
192//
193void DimWriteStatistics::UpdateService()
194{
195 Time previousTime;
196 uint64_t previousSize = 0;
197
198 while (1)
199 {
200 if (fUpdateInterval==0)
201 {
202 boost::this_thread::interruption_point();
203 boost::this_thread::yield();
204 continue;
205 }
206
207 Stats data;
208
209 for (set<string>::const_iterator it = fOpenedFiles.begin(); it != fOpenedFiles.end(); it++)
210 data.sizeWritten += GetFileSizeOnDisk(*it);
211 data.sizeWritten -= fBaseSize;
212
213 const Time cTime = Time();
214
215 data.freeSpace = GetFreeSpace();
216 data.rateWritten = data.sizeWritten-previousSize;
217 data.timeElapsed = (cTime - previousTime).total_milliseconds();
218
219 previousSize = data.sizeWritten;
220 previousTime = cTime;
221
222 fDimService.Update(data);
223
224 fStats = data;
225
226 if (fDebug)
227 {
228 ostringstream str;
229 str << "Written: " << fStats.sizeWritten/1000 << " kB; writing rate: ";
230 str << fStats.rateWritten/fStats.timeElapsed << " kB/s; free space: ";
231 str << fStats.freeSpace/1000000 << " MB";
232 fLog.Debug(str);
233 }
234
235 boost::this_thread::sleep(milliseconds(fUpdateInterval));
236 }
237}
238// --------------------------------------------------------------------------
239//
240//! Let the object know that a new file has been opened
241//! @param fileName the full name of the file newly opened
242//! @return whether this file could be stated or not
243//
244bool DimWriteStatistics::FileOpened(const string& fileName)
245{
246 if (fOpenedFiles.find(fileName) != fOpenedFiles.end())
247 return false;
248
249 //Add a newly opened file, and remember its original size
250 const int64_t newSize = GetFileSizeOnDisk(fileName);
251 if (newSize == -1)
252 return false;
253
254 fBaseSize += newSize;
255 fOpenedFiles.insert(fileName);
256
257 return true;
258}
259// --------------------------------------------------------------------------
260//
261//! Set the debug mode on and off
262//! @param debug the new mode (true or false)
263//
264void DimWriteStatistics::SetDebugMode(bool debug)
265{
266 fDebug = debug;
267
268 if (fDebug)
269 fLog.Debug("Debug mode is now on.");
270}
271// --------------------------------------------------------------------------
272//
273//! Set the update of the service interval
274//! @param duration the duration between two services update, in second
275//
276void DimWriteStatistics::SetUpdateInterval(const int16_t duration)
277{
278 if (!finite(duration))
279 {
280 fLog.Error("Provided update interval is not a valid float... discarding.");
281 return;
282 }
283 if (uint16_t(duration) == fUpdateInterval)
284 {
285 fLog.Warn("Statistics update interval not modified. Supplied value already in use.");
286 return;
287 }
288
289 if (duration <= 0)
290 fLog.Message("Statistics are now OFF.");
291 else
292 {
293 ostringstream str;
294 str << "Statistics update interval is now " << duration << " seconds";
295 fLog.Message(str);
296 }
297
298 fUpdateInterval = duration<0 ? 0 : duration;
299}
Note: See TracBrowser for help on using the repository browser.