rambrain
configreader.cpp
Go to the documentation of this file.
1 /* rambrain - a dynamical physical memory extender
2  * Copyright (C) 2015 M. Imgrund, A. Arth
3  * mimgrund (at) mpifr-bonn.mpg.de
4  * arth (at) usm.uni-muenchen.de
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "configreader.h"
21 
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <pwd.h>
25 #include <unistd.h>
26 #include <cstdlib>
27 #include <sys/statvfs.h>
28 #include <cstring>
29 #include <algorithm>
30 
31 namespace rambrain
32 {
33 
34 template<>
35 void configLine<global_bytesize>::setValue ( const string &str )
36 {
37  if ( matchType & regexMatcher::units ) {
38  regexMatcher regex;
39  long long intval = 0LL;
40  double dblval = 0.0;
41  string unit;
42 
43  if ( matchType & regexMatcher::floating ) {
44  auto splitted = regex.splitDoubleValueUnit ( str );
45  dblval = splitted.first;
46  unit = splitted.second;
47 
48  } else {
49  auto splitted = regex.splitIntegerValueUnit ( str );
50  intval = splitted.first;
51  unit = splitted.second;
52  }
53 
54  if ( unit == "b" || unit == "B" ) {
55  value = 1uLL;
56  } else if ( unit == "kb" || unit == "kB" || unit == "Kb" || unit == "KB" ) {
57  value = kib;
58  } else if ( unit == "mb" || unit == "Mb" || unit == "MB" ) {
59  value = mib;
60  } else if ( unit == "gb" || unit == "Gb" || unit == "GB" ) {
61  value = gig;
62  } else {
63  value = 1uLL;
64  }
65 
66  value *= ( ( matchType & regexMatcher::floating ) ? dblval : intval );
67  } else {
68  value = atoll ( str.c_str() );
69  }
70 }
71 
72 template<>
73 void configLine<bool>::setValue ( const string &str )
74 {
75  value = ! ( str == "false" || str == "False" || str == "FALSE" || str == "0" );
76 }
77 
78 template<>
79 void configLine<swapPolicy>::setValue ( const string &str )
80 {
81  if ( str == "fixed" ) {
82  value = swapPolicy::fixed;
83  } else if ( str == "autoextendable" ) {
84  value = swapPolicy::autoextendable;
85  } else if ( str == "interactive" ) {
86  value = swapPolicy::interactive;
87  } else {
88  value = swapPolicy::autoextendable;
89  }
90 }
91 
92 configuration::configuration() : memoryManager ( "memoryManager", "cyclicManagedMemory", regexMatcher::text ),
93  swap ( "swap", "managedFileSwap", regexMatcher::text ),
95  swapfiles ( "swapfiles", "rambrainswap-%d-%d", regexMatcher::swapfilename ),
96  memory ( "memory", 0, regexMatcher::floating | regexMatcher::units ),
97  swapMemory ( "swapMemory", 0, regexMatcher::floating | regexMatcher::units ),
98  enableDMA ( "enableDMA", false, regexMatcher::integer | regexMatcher::boolean ),
99  policy ( "policy", swapPolicy::autoextendable, regexMatcher::text )
100 {
101  // Fill configOptions
102  configOptions.push_back ( &memoryManager );
103  configOptions.push_back ( &swap );
104  configOptions.push_back ( &swapfiles );
105  configOptions.push_back ( &memory );
106  configOptions.push_back ( &swapMemory );
107  configOptions.push_back ( &enableDMA );
108  configOptions.push_back ( &policy );
109 
110  // Get free main memory
111  ifstream meminfo ( "/proc/meminfo", ifstream::in );
112  char line[1024];
113  for ( int c = 0; c < 3; ++c ) {
114  meminfo.getline ( line, 1024 );
115  }
116 
117  global_bytesize bytes_avail;
118 
119  char *begin = NULL, *end = NULL;
120  char *pos = line;
121  while ( !end ) {
122  if ( *pos <= '9' && *pos >= '0' ) {
123  if ( begin == NULL ) {
124  begin = pos;
125  }
126  } else {
127  if ( begin != NULL ) {
128  end = pos;
129  }
130  }
131  ++pos;
132  }
133  * ( end + 1 ) = 0x00;
134  bytes_avail = atol ( begin ) * 1024;
135 
136  memory.value = bytes_avail * 0.5;
137 
138  // Get free partition space
139  char exe[1024];
140  int ret;
141 
142  ret = readlink ( "/proc/self/exe", exe, sizeof ( exe ) - 1 );
143  if ( ret != -1 ) {
144  exe[ret] = '\0';
145  struct statvfs stats;
146  statvfs ( exe, &stats );
147 
148  swapMemory.value = stats.f_bfree * stats.f_bsize / 2;
149  }
150 }
151 
153 {
154  localConfigPath = getHomeDir() + "/.rambrain.conf";
155 }
156 
158 {
159  if ( !reopenStreams() ) {
160  return false;
161  }
162 
163  vector<configLineBase *> readLines;
164 
165  readSuccess = true;
166  for ( int i = 0; i < 3; ++i ) {
167  if ( readLines.size() < config.configOptions.size() ) {
168  readSuccess &= parseConfigFile ( streams[i], readLines );
169  }
170  }
171 
173 
174  return readSuccess;
175 }
176 
178 {
179  for ( int i = 0; i < 3; ++i ) {
180  if ( streams[i].is_open() ) {
181  streams[i].close();
182  }
183  }
184  bool readAConfig = false;
185 
186  streams[0].open ( customConfigPath );
187  if ( streams[0].is_open() ) {
188  infomsgf ( "Rambrain is initialized using custom config file: %s\n", customConfigPath.c_str() );
189  readAConfig = true;
190  }
191  streams[1].open ( localConfigPath );
192  if ( streams[1].is_open() ) {
193  infomsgf ( "Rambrain is initialized using user's config file: %s\n", localConfigPath.c_str() );
194  readAConfig = true;
195  }
196  streams[2].open ( globalConfigPath );
197  if ( streams[2].is_open() ) {
198  infomsgf ( "Rambrain is initialized using system wide config file: %s\n", globalConfigPath.c_str() );
199  readAConfig = true;
200  }
201 
202  if ( !readAConfig ) {
203  infomsg ( "Rambrain is initialized using default settings." );
204  }
205 
206  return streams[0].is_open() || streams[1].is_open() || streams[2].is_open();
207 }
208 
209 bool configReader::parseConfigFile ( istream &stream, vector<configLineBase *> &readLines )
210 {
211  string line;
212  bool ret = true, defaultDone = false, specificDone = false;
213  string appName = getApplicationName();
214  vector<configLineBase *> thisReadLines ( readLines );
215 
216  while ( stream.good() && ! ( defaultDone && specificDone ) ) {
217  getline ( stream, line );
219  if ( line.empty() ) {
220  continue;
221  }
222 
223  unsigned int current = stream.tellg();
224 
225  if ( regex.matchConfigBlock ( line ) ) {
226  ret &= parseConfigBlock ( stream, specificDone ? readLines : thisReadLines );
227 
228  defaultDone = true;
229  } else if ( !specificDone && regex.matchConfigBlock ( line, appName ) ) {
230  ret &= parseConfigBlock ( stream, readLines );
231 
232  specificDone = true;
233  }
234 
235  stream.seekg ( current );
236  }
237 
238  // Merge thisReadLines into readLines if a default has been done before, ergo memorize also elements from default and not specific
239  if ( defaultDone ) {
240  readLines.insert ( readLines.end(), thisReadLines.begin(), thisReadLines.end() );
241  unique ( readLines.begin(), readLines.end() );
242  }
243 
244  return ret;
245 }
246 
247 bool configReader::parseConfigBlock ( istream &stream, vector<configLineBase *> &readLines )
248 {
249  string line, first;
250 
251  while ( stream.good() ) {
252  getline ( stream, line );
254  if ( line.empty() ) {
255  continue;
256  }
257 
258  first = line.substr ( 0, 1 );
259  if ( first == "[" ) {
260  break;
261  } else if ( first == "#" ) {
262  continue;
263  } else {
264  bool matched = false;
265 
266  //Extract until a comment sign
267  size_t comment = line.find_first_of ( '#' );
268  if ( comment != line.npos ) {
269  line = line.substr ( 0, comment );
270  }
271 
272  for ( auto it = config.configOptions.begin(); it != config.configOptions.end(); ++it ) {
273  configLineBase *cl = *it;
274  std::pair<string, string> match = regex.matchKeyEqualsValue ( line, cl->name, cl->matchType );
275  if ( match.first == cl->name ) {
276  matched = true;
277 
278  if ( find ( readLines.begin(), readLines.end(), cl ) == readLines.end() ) { // Only if this config option has not been read yet
279  readLines.push_back ( cl );
280  cl->setValue ( match.second );
281  break;
282  }
283  }
284  }
285 
286  if ( !matched ) {
287  warnmsgf ( "Could not parse config line: %s\n", line.c_str() );
288  }
289  }
290  }
291 
292  return true;
293 }
294 
296 {
297  char exe[1024];
298  int ret;
299 
300  ret = readlink ( "/proc/self/exe", exe, sizeof ( exe ) - 1 );
301  if ( ret == -1 ) {
302  return "";
303  }
304  exe[ret] = '\0';
305 
306  string fullName ( exe );
307 
308  unsigned int found = fullName.find_last_of ( "/\\" );
309  return fullName.substr ( found + 1 );
310 }
311 
313 {
314  struct passwd *pw = getpwuid ( getuid() );
315  return pw->pw_dir;
316 }
317 
319 {
320  str.erase ( str.begin(), std::find_if ( str.begin(), str.end(), std::not1 ( std::ptr_fun<int, int> ( std::isspace ) ) ) );
321  str.erase ( std::find_if ( str.rbegin(), str.rend(), std::not1 ( std::ptr_fun<int, int> ( std::isspace ) ) ).base(), str.end() );
322 }
323 }
virtual void setValue(const string &str)
Reset the value from a string.
Definition: configreader.h:106
swapPolicy
An enumeration to regulate how the swap should define when it approaches it's set boundary...
Definition: configreader.h:47
configLine< swapPolicy > policy
Definition: configreader.h:141
bool reopenStreams()
(Re)Open streams to config files
configLine< bool > enableDMA
Definition: configreader.h:140
bool matchConfigBlock(const string &str, const string &blockname="default") const
Checks if a string matches the header of a configuration block.
pair< long long int, string > splitIntegerValueUnit(const string &str) const
Split a string containing value and possibly unit into both parts.
void stripLeadingTrailingWhitespace(string &str) const
Strips any leading and trailing whitespace from a given string.
uint64_t global_bytesize
Definition: common.h:65
bool parseConfigBlock(istream &stream, vector< configLineBase * > &readLines)
Parse an identified configuration block and extract all matching variables.
Class for config key value pairs represented by a line in a config file.
Definition: configreader.h:87
pair< string, string > matchKeyEqualsValue(const string &str, int valueType=alphanumtext) const
Checks if a string matches something like key = value.
configLine< string > swapfiles
Definition: configreader.h:138
bool parseConfigFile(istream &stream, vector< configLineBase * > &readLines)
Parse config file.
string getHomeDir() const
Look up the home directory of the current user.
const global_bytesize gig
Definition: common.h:69
const global_bytesize kib
Definition: common.h:67
const string globalConfigPath
Definition: configreader.h:259
const regexMatcher regex
Definition: configreader.h:263
string substituteHomeDir(const string &source, const string &homedir) const
Replace all occurences of a ~ by the absolute home directory.
Class to handle regex matching used for parsing configuration files.
Definition: regexmatcher.h:35
virtual void setValue(const string &str)=0
Reset the value from a string.
configLine< global_bytesize > memory
Definition: configreader.h:139
configLine< string > swap
Definition: configreader.h:138
bool readConfig()
Read in and parse config.
string getApplicationName() const
Extract the current binary's name out of the /proc file system.
configLine< string > memoryManager
Definition: configreader.h:138
pair< double, string > splitDoubleValueUnit(const string &str) const
Split a string containing value and possibly unit into both parts.
configuration config
Definition: configreader.h:267
configReader()
Init a new reader.
configLine< global_bytesize > swapMemory
Definition: configreader.h:139
vector< configLineBase * > configOptions
Definition: configreader.h:143
Base class for config lines.
Definition: configreader.h:57
const global_bytesize mib
Definition: common.h:68