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