Each Service has Protocol, Hostname, Login, Password, Shared_Ptr to Logging facility...
[bpdyndnsd] / src / config.cpp
1 /** @file
2  * @brief Config class implementation. This class represents the actual configuration.
3  *
4  *
5  *
6  * @copyright Intra2net AG
7  * @license GPLv2
8 */
9
10 #include "config.h"
11
12 // Following boost macros are needed for serialization of derived classes through a base class pointer (Service *).
13 BOOST_CLASS_EXPORT_GUID(ODS, "ODS")
14 BOOST_CLASS_EXPORT_GUID(DHS, "DHS")
15
16 namespace po = boost::program_options;
17 namespace fs = boost::filesystem;
18
19 using namespace std;
20
21 /**
22  * Default Constructor. Available command line and config file options with their default values are defined here.
23  */
24 Config::Config(LoggerPtr _log)
25     : Daemon_mode(false)
26     , Loglevel(0)
27     , Syslog(false)
28     , Config_path("/etc/bpdyndnsd")
29 {
30     // initialize Logger
31     Log = _log;
32
33     // Available service description config options
34     po::options_description opt_desc_service("Service description options");
35     opt_desc_service.add_options()
36         ("protocol",po::value<string>(),"The service protocol.")
37         ("host",po::value<string>(),"The hostname to update.")
38         ("login",po::value<string>(),"Login name.")
39         ("password",po::value<string>(),"Corresponding password.")
40     ;
41
42     // Available command line only options
43     po::options_description opt_desc_cmd_only("Command line only options");
44     opt_desc_cmd_only.add_options()
45         ("help,?","Show help.")
46         ("version,v","Show version.")
47         ("config,c",po::value<string>()->default_value("/etc/bpdyndnsd"),"Set the config path.")
48     ;
49
50      // Available generic options. Valid on cmd or in config file.
51     po::options_description opt_desc_generic("Generic config options");
52     opt_desc_generic.add_options()
53         ("daemon_mode",po::value<bool>()->default_value(false),"Run as system daemon.")
54         ("loglevel",po::value<int>()->default_value(0),"Loglevel.")
55         ("syslog",po::value<bool>()->default_value(false),"Use syslog facility.")
56     ;
57
58     // Define valid command line parameters
59     Opt_desc_cmd = new po::options_description("Command line options");
60     Opt_desc_cmd->add(opt_desc_cmd_only);
61     Opt_desc_cmd->add(opt_desc_generic);
62     Opt_desc_cmd->add(opt_desc_service);
63
64     // Define valid config file options
65     Opt_desc_conf_main = new po::options_description("Config file options");
66     Opt_desc_conf_main->add(opt_desc_generic);
67
68     // Define valid service file options
69     Opt_desc_conf_service = new po::options_description("Service file options");
70     Opt_desc_conf_service->add(opt_desc_service);
71
72     Log->print_constructor_call("Config");
73 }
74
75
76 /**
77  * Default Destructor
78  */
79 Config::~Config()
80 {
81     delete Opt_desc_cmd;
82     delete Opt_desc_conf_main;
83     delete Opt_desc_conf_service;
84
85     Log->print_destructor_call("Config");
86 }
87
88
89 /**
90  * This function serializes all Service objects in Services into a specified file.
91  * @return 0 if all is fine, -1 otherwise.
92  */
93 int Config::serialize_services()
94 {
95     //TODO: error handling
96
97     cout << "starting serialize objects" << endl;
98     // serialize service objects
99     ofstream ofs("/home/bjoern/intranator/bpdyndnsd/objects.ser");
100     boost::archive::text_oarchive oa(ofs);
101
102     BOOST_FOREACH(ServicePtr service, Services)
103     {
104         Service* ptr = service.get();
105         oa << BOOST_SERIALIZATION_NVP(ptr);
106     }
107     ofs.close();
108
109     return 0;
110 }
111
112
113 /**
114  * Parses the command line arguments and does the needed actions.
115  * @param argc Command line argument number given to main.
116  * @param argv[] Pointer to command line argument array given to main.
117  * @return 0 if all is fine, 1 if not.
118  */
119 int Config::parse_cmd_line(int argc, char *argv[])
120 {
121     try
122     {
123         po::store(po::parse_command_line(argc, argv, *this->Opt_desc_cmd), Variables_map);
124         po::notify(Variables_map);
125
126         if ( Variables_map.count("help") )
127         {
128             Log->print_usage(Opt_desc_cmd);
129             return 1;
130         }
131         else if ( Variables_map.count("version") )
132         {
133             Log->print_version();
134             return 1;
135         }
136
137         // Create a service object if all needed options are set on the command line
138         if ( Variables_map.count("protocol") && Variables_map.count("host") && Variables_map.count("login") && Variables_map.count("password") )
139         {
140             // Get the cmd parameter values for protocol host login and password
141             string protocol = Variables_map["protocol"].as<string>();
142             string host = Variables_map["host"].as<string>();
143             string login = Variables_map["login"].as<string>();
144             string password = Variables_map["password"].as<string>();
145
146             //TODO: convert protocol option to lowercase
147
148             ServicePtr service = create_service(protocol,host,login,password);
149             if ( service )
150                 Services.push_back(service);
151             else
152                 return 1;
153         }
154         else if ( Variables_map.count("protocol") || Variables_map.count("host") || Variables_map.count("login") || Variables_map.count("password") )
155         {
156             Log->print_missing_cmd_service_option();
157             Log->print_usage(Opt_desc_cmd);
158             return 1;
159         }
160
161         if ( Variables_map.count("config") )
162         {
163             fs::path full_config_path = fs::system_complete(fs::path(Variables_map["config"].as<string>()));
164             Config_path = full_config_path.string();
165             if ( !fs::exists(full_config_path) ||  !fs::is_directory(full_config_path) )
166             {
167                 // Config path doesn't exist or is not a directory
168                 Log->print_error_config_path(Config_path);
169                 return 1;
170             }
171         }
172
173         if ( Variables_map.count("loglevel") )
174             Loglevel = Variables_map["loglevel"].as<int>();
175
176         if ( Variables_map.count("syslog") )
177             Syslog = Variables_map["syslog"].as<bool>();
178
179     }
180     catch(po::unknown_option e)
181     {
182         Log->print_unknown_cmd_option(e.what());
183         Log->print_usage(Opt_desc_cmd);
184         return 1;
185     }
186     return 0;
187 }
188
189
190 /**
191  * 
192  * @param protocol 
193  * @param host 
194  * @param login 
195  * @param password 
196  * @return A pointer to the created Service object.
197  */
198 ServicePtr Config::create_service(const string &protocol,const string &hostname, const string &login, const string &password)
199 {
200     if(protocol == "dhs")
201     {
202         ServicePtr service_dhs(new DHS(protocol,hostname,login,password,Log,0,0,1));
203         return service_dhs;
204     }
205     else if(protocol == "ods")
206     {
207         ServicePtr service_ods(new ODS(protocol,hostname,login,password,Log,0,0,1));
208         return service_ods;
209     }
210     else
211     {
212         Log->print_unknown_protocol(protocol);
213         ServicePtr service;
214         return service;
215     }
216 }
217
218
219 /**
220  * Loads a service config file, invoked by load_config_from_files.
221  * @param full_filename Filename of the service config file to load.
222  * @return 0 if all is fine, 3 if an unknown option was detected, 4 if the service file could not be opened for reading.
223  */
224 int Config::load_service_config_file(const string& full_filename)
225 {
226     Log->print_load_service_conf(full_filename);
227
228     ifstream service_config_file(full_filename.c_str(),ifstream::in);
229     if(service_config_file.is_open())
230     {
231         try
232         {
233             po::variables_map vm;
234             po::parsed_options parsed_service_options = po::parse_config_file(service_config_file,*this->Opt_desc_conf_service,true);
235             po::store(parsed_service_options,vm);
236             po::notify(vm);
237
238             if(vm.count("protocol") && vm.count("host") && vm.count("login") && vm.count("password"))
239             {
240                 // create the corresponding service
241                 string protocol = vm["protocol"].as<string>();
242                 string host = vm["host"].as<string>();
243                 string login = vm["login"].as<string>();
244                 string password = vm["password"].as<string>();
245
246                 // TODO: convert protocol to lowercase
247                 //protocol = tolower(protocol.c_str());
248
249                 ServicePtr service = create_service(protocol,host,login,password);
250                 if ( service )
251                 {
252                     Services.push_back(service);
253                 }
254             }
255         }
256         catch ( po::unknown_option e )
257         {
258             // unknown option in config file detected
259             service_config_file.close();
260             Log->print_unknown_service_conf_option(e.what());
261             return 1;
262         }
263         service_config_file.close();
264     }
265     else
266     {
267         // error opening service config file for reading
268         Log->print_error_opening(full_filename);
269         return 1;
270     }
271     return 0;
272 }
273
274
275 /**
276  * Loads the main config file, invoked by load_config_from_files
277  * @param full_filename The full filename of the main config file to load
278  * @return 0 if all is fine. 3 if unknown option was detected, 4 if main config file could not be opened for reading
279  */
280 int Config::load_main_config_file(const string& full_filename)
281 {
282     Log->print_load_main_conf(full_filename);
283
284     ifstream main_config_file(full_filename.c_str(),ifstream::in);
285     if(main_config_file.is_open())
286     {
287         try
288         {
289             po::parsed_options parsed_main_options = po::parse_config_file(main_config_file,*this->Opt_desc_conf_main,true);
290             po::store(parsed_main_options,Variables_map);
291             po::notify(Variables_map);
292
293             if(Variables_map.count("daemon_mode") && Variables_map.count("loglevel") && Variables_map.count("syslog"))
294             {
295                 Daemon_mode = Variables_map["daemon_mode"].as<bool>();
296                 Loglevel = Variables_map["loglevel"].as<int>();
297                 Syslog = Variables_map["syslog"].as<bool>();
298             }
299         }
300         catch ( po::unknown_option e )      // at the moment 04-08-2009 this exception is never thrown :-(
301         {
302             // unknown option in main config file detected
303             main_config_file.close();
304             Log->print_unknown_main_conf_option(e.what());
305             return 1;
306         }
307         main_config_file.close();
308     }
309     else
310     {
311         // error opening main config file for reading
312         Log->print_error_opening(full_filename);
313         return 1;
314     }
315     return 0;
316 }
317
318
319 /**
320  * Loads the main and the service config file and does the needed action.
321  * @param config_path The path to the config directory.
322  * @return 0 if all is fine.
323  */
324 int Config::load_config_from_files()
325 {
326     fs::path full_config_path = fs::path(Config_path);
327
328     fs::directory_iterator end_iter;
329     for ( fs::directory_iterator dir_itr(full_config_path) ; dir_itr != end_iter ; ++dir_itr )
330     {
331         if( fs::is_regular_file( dir_itr->status() ) )
332         {
333             string actual_file = dir_itr->path().filename();
334             boost::regex expr(".*\\.conf?");
335              // If it is the main config file do the following
336             if ( actual_file == "bpdyndnsd.conf" )
337             {
338                 // Load the main config file
339                 string full_filename = dir_itr->path().string();
340                 if ( load_main_config_file(full_filename) != 0 )
341                     return 1;
342             }
343             // If it is a service definition file *.conf, parse it and generate the corresponding service
344             else if ( boost::regex_search( actual_file,expr ) )
345             {
346                 string full_filename = dir_itr->path().string();
347                 if ( load_service_config_file(full_filename) != 0 )
348                     return 1;
349             }
350         }
351     }
352     // Config file successfully loaded
353     Log->print_conf_loaded(Config_path);
354     return 0;
355 }
356
357
358 /**
359  * Getter method for Service list member.
360  * @return Pointer to a list of Service's.
361  */
362 list<ServicePtr> Config::get_services()
363 {
364     return this->Services;
365 }
366
367
368 /**
369  * Getter method for member Opt_desc_cmd.
370  * @return options_description*.
371  */
372 po::options_description* Config::get_opt_desc_cmd()
373 {
374     return Opt_desc_cmd;
375 }
376
377
378 /**
379  * Getter method for member Opt_desc_conf_main.
380  * @return options_description*.
381  */
382 po::options_description* Config::get_opt_desc_conf_main()
383 {
384     return Opt_desc_conf_main;
385 }
386
387
388 /**
389  * Getter method for member Opt_desc_conf_service.
390  * @return options_description*.
391  */
392 po::options_description* Config::get_opt_desc_conf_service()
393 {
394     return Opt_desc_conf_service;
395 }
396
397
398 /**
399  * Getter for member Loglevel.
400  * @return Member Loglevel.
401  */
402 int Config::get_loglevel()
403 {
404     return Loglevel;
405 }
406
407
408 /**
409  * Getter for member Daemon_mode.
410  * @return TRUE if enabled, FALSE if disabled.
411  */
412 bool Config::get_daemon_mode()
413 {
414     return Daemon_mode;
415 }
416
417
418 /**
419  * Resets all shared Service pointers and clears the Services list.
420  */
421 void Config::delete_services()
422 {
423     BOOST_FOREACH( ServicePtr service, Services )
424     {
425         service.reset();
426     }
427     Services.clear();
428 }
429
430
431 /**
432  * Deletes the map with the previously parsed options.
433  * This is needed in case we reload the config and don't want the old cmd options to overwrite new config file options.
434  */
435 void Config::delete_variables_map()
436 {
437     Variables_map.clear();
438
439     po::variables_map _variables_map;
440     Variables_map = _variables_map;
441 }
442
443
444 /**
445  * Getter for member Syslog.
446  * @return True if logging through syslog is enabled, false otherwise.
447  */
448 bool Config::get_syslog()
449 {
450     return Syslog;
451 }