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