Merge branch 'daemon-ext'
[libi2ncommon] / configlib / i2n_global_config.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  *
22  * @author Reinhard Pfau
23  * 
24  * @copyright © Copyright 2008 by Intra2net AG
25  */
26
27 #include "i2n_global_config.hpp"
28
29
30 #include <set>
31 #include <map>
32
33 #include <filefunc.hxx>
34 #include <stringfunc.hxx>
35 #include <i2n_configdata.hpp>
36 #include <i2n_configfile.hpp>
37 #include <logfunc.hpp>
38 #include <tracefunc.hpp>
39
40 //#define NOISEDEBUG
41 #undef NOISEDEBUG
42
43 #ifdef NOISEDEBUG
44 #include <iostream>
45 #include <iomanip>
46 #define DOUT(msg) std::cout << msg << std::endl
47 #else
48 #define DOUT(msg) do {} while (0)
49 #endif
50
51
52 namespace I2n
53 {
54
55 namespace Config
56 {
57
58
59 namespace Detail
60 {
61
62
63 } // eo namespace Detail
64
65
66
67 namespace
68 {
69
70
71 Logger::PartLogger& module_logger()
72 {
73     static Logger::PartLogger _logger(HERE);
74     return _logger;
75 } // eo module_logger()
76
77
78
79 std::string config_file_name;
80
81 ConfigData config_data;
82
83
84 /*
85 **
86 */
87
88
89 struct HideOut
90 : public Detail::Thing
91 {
92     HideOut(
93         const std::string& section,
94         const std::string& key,
95         const Detail::FeedFunction& feed,
96         const Detail::FeedFunction2& feed2);
97
98     virtual ~HideOut();
99
100     Detail::FeedFunction m_feed;
101     Detail::FeedFunction2 m_feed2;
102     std::string  m_section;
103     std::string  m_key;
104 }; // eo struct HideOut
105
106 typedef boost::shared_ptr< HideOut > HideOutPtr;
107
108
109 class HideOutMap
110 : public GroupedKeyValueData<
111     std::string,
112     std::string,
113     HideOut*
114 >
115 {
116     public:
117         HideOutMap();
118         virtual ~HideOutMap();
119
120     public:
121
122         static int Instances;
123 }; // eo class HideOutMap
124
125 int HideOutMap::Instances= 0;
126
127 HideOutMap::HideOutMap()
128 {
129     DOUT("HideOutMap::HideOutMap()");
130     ++Instances;
131 } // eo HideOutMap::HideOutMap()
132
133
134 HideOutMap::~HideOutMap()
135 {
136     DOUT("HideOutMap::~HideOutMap()");
137     --Instances;
138 } // eo HideOutMap::~HideOutMap()
139
140
141 HideOutMap& hideout_map()
142 {
143     static HideOutMap the_hideout_map;
144     DOUT("hideout_map()");
145     return the_hideout_map;
146 } // eo hideout_map()
147
148
149 // implementation of HideOut:
150
151
152 HideOut::HideOut(
153     const std::string& section,
154     const std::string& key,
155     const Detail::FeedFunction& feed,
156     const Detail::FeedFunction2& feed2)
157 : m_feed(feed)
158 , m_feed2(feed2)
159 , m_section(section)
160 , m_key(key)
161 {
162     hideout_map()[section][key]+= this;
163 } // eo HideOut::HideOut(const FeedFunction&)
164
165
166
167 HideOut::~HideOut()
168 {
169     if (HideOutMap::Instances)
170     {
171         hideout_map()[m_section].removeValue(m_key,this);
172     }
173 } // eo HideOut::~HideOut()
174
175
176
177 /*
178 **
179 */
180
181
182 /**
183  * @brief distributes (new) values to the active config variables.
184  * @param old_config_data reference to the old config data (for determining the deltas).
185  * @return @a true if new values could be converted without an error.
186  */
187 bool feedEm( ConfigData& old_config_data )
188 {
189     SCOPETRACKER();
190     bool result= true;
191     HideOutMap::KeyListType section_list;
192     hideout_map().getKeys(section_list);
193     for(HideOutMap::KeyListType::const_iterator section_it = section_list.begin();
194         section_it != section_list.end();
195         ++section_it)
196     {
197         std::string section( *section_it );
198         DOUT("process section \"" << section << "\"");
199         HideOutMap::GroupDataType::KeyListType key_list;
200         hideout_map()[section].getKeys(key_list);
201         for(HideOutMap::GroupDataType::KeyListType::const_iterator key_it= key_list.begin();
202             key_it != key_list.end();
203             ++key_it)
204         {
205             std::string key( *key_it );
206             DOUT("process key \"" << key << "\"");
207             bool has_new_data= config_data.hasKey(section, key);
208             bool has_old_data= old_config_data.hasKey(section, key);
209             if (not has_new_data and not has_old_data)
210             {
211                 // we don't have data and we didn't had data... well, nothing to do:
212                 DOUT("no data");
213                 continue;
214             }
215             if (has_new_data)
216             {
217                 DOUT("has (new) data");
218                 StringList values;
219                 config_data[section].getValues(key, values);
220                 std::string value( config_data[section][key] );
221                 if (has_old_data)
222                 {
223                     StringList old_values;
224                     old_config_data[section].getValues(key, old_values);
225                     if (values == old_values)
226                     {
227                         // value didn't change, so we can skip this case.
228                         DOUT("data is same as before");
229                         continue;
230                     }
231                 }
232                 // at this point we need to publish (new or changed) value:
233                 HideOutMap::GroupDataType::ValueListType func_list;
234                 hideout_map()[section].getValues(key, func_list);
235                 for(HideOutMap::GroupDataType::ValueListType::iterator func_it= func_list.begin();
236                     func_it != func_list.end();
237                     ++func_it)
238                 {
239                     DOUT("  feed iteration");
240                     if (not (*func_it)) //paranoia
241                     {
242                         continue;
243                     }
244                     if ( (*func_it)->m_feed)
245                     {
246                         if ( not (*func_it)->m_feed( value ) )
247                         {
248                             module_logger().error() << "unable to publish [" <<section << "]"
249                                 << " \"" << key << "\"  : \"" << value << "\""
250                             ;
251                             result= false;
252                         }
253                         continue;
254                     }
255                     if ( (*func_it)->m_feed2)
256                     {
257                         if ( not (*func_it)->m_feed2( values ) )
258                         {
259                             module_logger().error() << "unable to publish [" <<section << "]"
260                                 << " \"" << key << "\"  : ...\"" << value << "\""
261                             ;
262                             result= false;
263                         }
264                         continue;
265                     }
266                     module_logger().error() << "no feeder";
267                 }
268                 continue;
269             }
270             // at this point we don't have (new) data, but we had old data...
271             {
272                 DOUT("had old data");
273                 StringList values;
274
275                 // at this point we need to publish (vanished) value:
276                 HideOutMap::GroupDataType::ValueListType func_list;
277                 hideout_map()[section].getValues(key, func_list);
278                 for(HideOutMap::GroupDataType::ValueListType::iterator func_it= func_list.begin();
279                     func_it != func_list.end();
280                     ++func_it)
281                 {
282                     DOUT("  feed iteration");
283                     if (not (*func_it)) //paranoia
284                     {
285                         continue;
286                     }
287                     if ( (*func_it)->m_feed)
288                     {
289                         //TODO reset the data (to initial value) somehow?
290                         // for now; we just keep the last value...
291                         continue;
292                     }
293                     if ( (*func_it)->m_feed2)
294                     {
295                         if ( not (*func_it)->m_feed2( values ) )
296                         {
297                             module_logger().error() << "unable to publish [" <<section << "]"
298                                 << " \"" << key << "\"  : <empty> "
299                             ;
300                             result= false;
301                         }
302                         continue;
303                     }
304                     module_logger().error() << "no feeder";
305                 }
306                 continue;
307             }
308         }
309     }
310     return result;
311 } // eo feedEm(ConfigData&,ConfigData&)
312
313
314 /*
315 **
316 */
317
318
319 } // eo namespace <anonymous>
320
321
322 /************************************************************************\
323 \************************************************************************/
324
325
326 namespace Detail
327 {
328
329
330 FlowerShop seymour(
331     const std::string& section,
332     const std::string& key,
333     const FeedFunction& feed)
334 {
335     DOUT("seymor(\"" << section << "\", \"" << key << "\",FeedFunction)");
336     FlowerShop result( new HideOut( section, key, feed, 0 ) );
337     if (not config_data.empty())
338     {
339         if (config_data.hasKey(section, key))
340         {
341             feed( config_data[section][key] );
342         }
343     }
344     return result;
345 } // eo seymour(const std::string&,const std::string&,const FeedFunction&)
346
347
348 FlowerShop seymour(
349     const std::string& section,
350     const std::string& key,
351     const FeedFunction2& feed)
352 {
353     DOUT("seymor(\"" << section << "\", \"" << key << "\",FeedFunction2)");
354     FlowerShop result( new HideOut( section, key, 0, feed ) );
355     if (not config_data.empty())
356     {
357         if (config_data.hasKey(section, key))
358         {
359             StringList values;
360             config_data[section].getValues(key, values);
361             feed( values );
362         }
363     }
364     return result;
365 } // eo seymour(const std::string&,const std::string&,const FeedFunction2&)
366
367
368 FlowerShop seymour(FlowerShop shop, const FeedFunction& feed)
369 {
370     if (!shop)
371     {
372         // no shop; no coordinates; no useful stuff to do...
373         return shop;
374     }
375     HideOutPtr ptr = boost::dynamic_pointer_cast< HideOut >(shop);
376     if (!ptr)
377     {
378         // again, no coordinates...
379         return ptr;
380     }
381     FlowerShop result( new HideOut( ptr->m_section, ptr->m_key, feed, 0 ) );
382     if (not config_data.empty())
383     {
384         if (config_data.hasKey(ptr->m_section, ptr->m_key))
385         {
386             feed( config_data[ptr->m_section][ptr->m_key] );
387         }
388     }
389     return result;
390 } // eo seymour(FlowerShop,const FeedFunction&)
391
392
393 FlowerShop seymour(FlowerShop shop, const FeedFunction2& feed)
394 {
395     if (!shop)
396     {
397         // no shop; no coordinates; no useful stuff to do...
398         return shop;
399     }
400     HideOutPtr ptr = boost::dynamic_pointer_cast< HideOut >(shop);
401     if (!ptr)
402     {
403         // again, no coordinates...
404         return ptr;
405     }
406     FlowerShop result( new HideOut( ptr->m_section, ptr->m_key, 0, feed ) );
407     if (not config_data.empty())
408     {
409         if (config_data.hasKey(ptr->m_section, ptr->m_key))
410         {
411             StringList values;
412             config_data[ptr->m_section].getValues(ptr->m_key, values);
413             feed( values );
414         }
415     }
416     return result;
417 } // eo seymour(FlowerShop,const FeedFunction&)
418
419
420 } // eo namespace Detail
421
422
423
424 /**
425  * @brief reloads the configuration from the last (known) config file.
426  * @return @a true if config was succesfully reloaded.
427  */
428 bool reload()
429 {
430     ConfigData old_config_data;
431     ConfigData new_config_data;
432     if (config_file_name.empty())
433     {
434         return false;
435     }
436     bool loaded= load_ini_config_file(config_file_name, new_config_data);
437     bool publish_successful= true;
438     if (loaded)
439     {
440         module_logger().debug() << "new_config_data.empty(): " << new_config_data.empty();
441         // save old data
442         old_config_data.swap( config_data );
443         // and install the new data
444         new_config_data.swap( config_data );
445
446         // react on the config: publish the values:
447         publish_successful = feedEm( old_config_data );
448     }
449     return loaded and publish_successful;
450 } // reload()
451
452
453
454
455 /**
456  * @brief sets the path of the config file and loads it.
457  * @param path the path to the config file.
458  * @return @a true iff the config could be loaded and all values pushed into their config vars.
459  */
460 bool set_config_file(const std::string& path)
461 {
462     if (not config_file_name.empty())
463     {
464         // WTF?!...
465         // a new config file.. well... why not?
466     }
467     Stat stat(path);
468     if (not stat or not stat.is_regular_file())
469     {
470         return false;
471     }
472     config_file_name= path;
473     return reload();
474 } // eo set_config_file(const std::string&)
475
476
477 /**
478  * @brief returns the current config file path.
479  * @return the current path.
480  */
481 std::string get_config_file()
482 {
483     return config_file_name;
484 } // eo get_config_file()
485
486
487 } // eo namespace config
488
489 } // eo namespace I2n