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