c1e58f1e811a5da5995acabaab1acdfb5eb2f8fb
[profile/ivi/automotive-message-broker.git] / ambd / pluginloader.cpp
1 /*
2 Copyright (C) 2012 Intel Corporation
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19
20 #include "pluginloader.h"
21 #include "glibmainloop.h"
22 #include "core.h"
23
24 #include <json.h>
25 #include <gio/gio.h>
26 #include <picojson.h>
27
28 #include <iostream>
29 #include <stdexcept>
30 #include <boost/concept_check.hpp>
31
32 std::string get_file_contents(const char *filename)
33 {
34         std::ifstream in(filename, std::ios::in);
35         if(!in.is_open())
36         {
37                 DebugOut(DebugOut::Error) << "Failed to open file: " << filename << endl;
38                 return "";
39         }
40
41         std::string output;
42         std::string line;
43         while(in.good())
44         {
45                 getline(in,line);
46                 output.append(line);
47         }
48         return output;
49 }
50
51 PluginLoader::PluginLoader(string configFile, int argc, char** argv): f_create(NULL), routingEngine(nullptr), mMainLoop(nullptr)
52 {
53         DebugOut()<<"Loading config file: "<<configFile<<endl;
54         std::string configBuffer = get_file_contents(configFile.c_str());
55
56         std::string picojsonerr = "";
57         picojson::value v;
58         picojson::parse(v, configBuffer.begin(), configBuffer.end(), &picojsonerr);
59
60         if(!picojsonerr.empty())
61         {
62                 DebugOut(DebugOut::Error) << "Failed to parse main config! " << endl;
63                 throw std::runtime_error("Error parsing config");
64         }
65
66         if(v.contains("routingEngine"))
67         {
68                 string restr = v.get("routingEngine").to_str();
69
70                 routingEngine = loadRoutingEngine(restr);
71
72                 if(!routingEngine)
73                 {
74                         DebugOut(DebugOut::Warning)<<"Failed to load routing engine plugin: "<<restr<<endl;
75                 }
76         }
77
78         if(!routingEngine)
79         {
80                 /// there is no mainloop entry, use default glib
81                 DebugOut()<<"No routing engine specified in config.  Using built-in 'core' routing engine by default."<<endl;
82
83                 /// core wants some specific configuration settings:
84                 std::map<std::string,std::string> settings;
85
86
87                 for (auto q : {"lowPriorityQueueSize", "normalPriorityQueueSize", "highPriorityQueueSize"})
88                 {
89                         if (v.contains(q))
90                         {
91                                 string restr = v.get(q).to_str();
92                                 settings[q] = restr;
93                         }
94                 }
95
96                 routingEngine = new Core(settings);
97         }
98
99
100         if(v.contains("plugins"))
101         {
102                 std::string pluginsPath = v.get("plugins").to_str();
103                 scanPluginDir(pluginsPath);
104         }
105
106         if(v.contains("mainloop"))
107         {
108                 /// there is a mainloop entry.  Load the plugin:
109
110                 string mainloopstr = v.get("mainloop").to_str();
111
112                 mMainLoop = loadMainLoop(mainloopstr, argc, argv);
113
114                 if(!mMainLoop)
115                 {
116                         DebugOut(DebugOut::Warning)<<"Failed to load main loop plugin."<<endl;
117                 }
118         }
119         else if(!mMainLoop)
120         {
121                 /// there is no mainloop entry, use default glib
122                 DebugOut()<<"No mainloop specified in config.  Using glib by default."<<endl;
123                 mMainLoop = new GlibMainLoop(argc, argv);
124         }
125
126
127         for (auto q : {"sources", "sinks"})
128         {
129                 if(v.contains("sources"))
130                 {
131                         picojson::array list = v.get(q).get<picojson::array>();
132                         if (!list.size())
133                         {
134                                 DebugOut() << "Error getting list for " << q << endl;
135                                 throw std::runtime_error("Error getting sources list");
136                         }
137
138                         for(auto src : list)
139                         {
140                                 std::map<std::string, std::string> configurationMap;
141                                 for( auto obj : src.get<picojson::object>())
142                                 {
143                                         string valstr = obj.second.to_str();
144                                         string key = obj.first;
145
146                                         DebugOut() << "plugin config key: " << key << "value:" << valstr << endl;
147
148                                         configurationMap[key] = valstr;
149                                 }
150
151                                 string path = configurationMap["path"];
152
153                                 if(!loadPlugin(path, configurationMap))
154                                         DebugOut(DebugOut::Warning) << "Failed to load plugin: " << path <<endl;
155                         }
156
157                 }
158         }
159
160         Core* core = static_cast<Core*>(routingEngine);
161         if( core != nullptr )
162         {
163                 core->inspectSupported();
164         }
165 }
166
167 PluginLoader::~PluginLoader()
168 {
169         for(auto handle : openHandles)
170                 dlclose(handle);
171 }
172
173 IMainLoop *PluginLoader::mainloop()
174 {
175         return mMainLoop;
176 }
177
178 std::string PluginLoader::errorString()
179 {
180         return mErrorString;
181 }
182
183 void PluginLoader::scanPluginDir(const std::string & dir)
184 {
185         DebugOut() << "Scanning plugin directory: " << dir << endl;
186
187         auto pluginsDirectory = amb::make_gobject(g_file_new_for_path(dir.c_str()));
188
189         GError* enumerateError = nullptr;
190
191         auto enumerator = amb::make_gobject(g_file_enumerate_children(pluginsDirectory.get(), G_FILE_ATTRIBUTE_ID_FILE,
192                                                                                                                                   G_FILE_QUERY_INFO_NONE, nullptr,
193                                                                                                                                   &enumerateError));
194         auto enumerateErrorPtr = amb::make_super(enumerateError);
195
196         if(enumerateErrorPtr)
197         {
198                 DebugOut(DebugOut::Error) << "Scanning plugin directory: " << enumerateErrorPtr->message << endl;
199                 return;
200         }
201
202         GError* errorGetFile = nullptr;
203         while(auto pluginConfig = amb::make_gobject(g_file_enumerator_next_file(enumerator.get(), nullptr, &errorGetFile)))
204         {
205                 std::string name = g_file_info_get_name(pluginConfig.get());
206
207                 DebugOut() << "Found file: " << name << endl;
208                 std::string fullpath = dir + (boost::algorithm::ends_with(dir, "/") ? "":"/") + name;
209                 std::string data = get_file_contents(fullpath.c_str());
210
211                 DebugOut() << "data: " << data << endl;
212
213                 if(!readPluginConfig(data))
214                 {
215                         DebugOut(DebugOut::Error) << "Reading contentds of file: " << name << endl;
216                 }
217         }
218
219         auto errorGetFilePtr = amb::make_super(errorGetFile);
220
221         if(errorGetFilePtr)
222         {
223                 DebugOut(DebugOut::Error) << "enumerating file: " << errorGetFilePtr->message << endl;
224                 return;
225         }
226 }
227
228 bool PluginLoader::readPluginConfig(const string &configData)
229 {
230         picojson::value v;
231         std::string err;
232
233         picojson::parse(v, configData.begin(), configData.end(), &err);
234
235         if (!err.empty())
236         {
237                 DebugOut(DebugOut::Error) << err << endl;
238                 return false;
239         }
240
241         std::string pluginName;
242         if(v.contains("name"))
243         {
244                 pluginName = v.get("name").to_str();
245         }
246
247         std::string pluginPath;
248         if(v.contains("path"))
249         {
250                 pluginPath = v.get("path").to_str();
251         }
252         else
253         {
254                 DebugOut(DebugOut::Error) << "config missing 'path'." << endl;
255                 return false;
256         }
257
258         bool enabled = false;
259         if(v.contains("enabled"))
260         {
261                 enabled = v.get("enabled").get<bool>();
262         }
263         else
264         {
265                 DebugOut(DebugOut::Error) << "config missing 'enabled'." << endl;
266                 return false;
267         }
268
269         DebugOut() << "Plugin: " << pluginName << endl;
270         DebugOut() << "Path: " << pluginPath << endl;
271         DebugOut() << "Enabled: " << enabled << endl;
272
273         if(enabled)
274         {
275                 std::map<std::string, std::string> otherConfig;
276
277                 picojson::object obj = v.get<picojson::object>();
278                 for(auto itr : obj)
279                 {
280                         otherConfig[itr.first] = itr.second.to_str();
281                 }
282
283                 loadPlugin(pluginPath, otherConfig);
284         }
285
286         return true;
287 }
288