merge with master
[platform/framework/web/wrt-plugins-common.git] / src / plugins-installer / plugin_installer.cpp
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /**
17  * @file    plugin_installer.cpp
18  * @author  Andrzej Surdej(a.surdej@samgsung.com)
19  * @version
20  * @brief
21  */
22
23 #include "plugin_installer.h"
24
25 #include <sys/stat.h>
26 #include <cstdio>
27 #include <dlfcn.h>
28 #include <string>
29
30 #include <vcore/VCore.h>
31 #include <libxml/parser.h>
32
33 #include "plugin_objects.h"
34 #include "plugin_metafile_reader.h"
35 #include "plugin_installer_errors.h"
36
37 #include <Plugin.h>
38 #include <IObject_cast.h>
39 //#include <SoFeatures.h>
40 #include <plugin_utils.h>
41 #include <ExportedApi.h>
42
43 #include <wrt-commons/wrt_plugin_export.h>
44
45 #include <dpl/log/log.h>
46 #include <dpl/exception.h>
47 #include <dpl/wrt-dao-ro/global_config.h>
48 #include <dpl/wrt-dao-rw/plugin_dao.h>
49 #include <dpl/wrt-dao-rw/feature_dao.h>
50 #include <dpl/foreach.h>
51 #include <dpl/wrt-dao-ro/WrtDatabase.h>
52
53 #include <dpl/singleton_impl.h>
54
55 using namespace WrtDB;
56 using namespace WrtPluginsApi;
57
58 IMPLEMENT_SINGLETON(PluginsInstaller)
59
60 namespace {
61 const std::string DIRECTORY_SEPARATOR = std::string("/");
62 }
63
64 const int PluginsInstaller::INSTALLATION_ERROR = -1;
65
66 PluginsInstaller::PluginsInstaller() :
67     m_initialized(false)
68 {
69     LogInfo("PluginsInstaller created.");
70 }
71 PluginsInstaller::~PluginsInstaller()
72 {
73     LogInfo("PluginsInstaller destroyed.");
74 }
75
76 void PluginsInstaller::checkDatabaseTablesExistance()
77 {
78     if (!WrtDB::WrtDatabase::CheckTableExist("FeaturesList") ||
79         !WrtDB::WrtDatabase::CheckTableExist("PluginProperties") ||
80         !WrtDB::WrtDatabase::CheckTableExist("PluginDependencies") ||
81         !WrtDB::WrtDatabase::CheckTableExist("PluginImplementedObjects") ||
82         !WrtDB::WrtDatabase::CheckTableExist("PluginRequiredObjects") ||
83         !WrtDB::WrtDatabase::CheckTableExist("DeviceCapabilities") ||
84         !WrtDB::WrtDatabase::CheckTableExist("FeatureDeviceCapProxy"))
85     {
86         LogError("Wrong database. Required tables not exist.");
87         deinitialize();
88         Assert(false && "Wrong database. Required tables not exist.");
89     }
90 }
91
92 bool PluginsInstaller::initialize()
93 {
94     LogDebug("Initializing required systems.");
95
96     // Check paths
97     if (!PluginUtils::checkPaths()) {
98         return false;
99     }
100     // Initialize ValidationCore - this must be done before AttachDatabases
101     ValidationCore::VCoreInit(
102         std::string(GlobalConfig::GetFingerprintListFile()),
103         std::string(GlobalConfig::GetFingerprintListSchema()),
104         std::string(GlobalConfig::GetVCoreDatabaseFilePath()));
105
106     xmlInitParser();
107     WrtDB::WrtDatabase::attachToThreadRW();
108     ValidationCore::AttachToThreadRW();
109     checkDatabaseTablesExistance();
110     LogDebug("Initialized.");
111     m_initialized = true;
112     return true;
113 }
114
115 void PluginsInstaller::deinitialize()
116 {
117     LogDebug("Shuting systems down.");
118     m_initialized = false;
119     ValidationCore::DetachFromThread();
120     WrtDB::WrtDatabase::detachFromThread();
121     ValidationCore::VCoreDeinit();
122     xmlCleanupParser();
123     LogDebug("Done");
124 }
125
126 PluginsInstaller::ReturnStatus PluginsInstaller::installPlugin(
127     const std::string& libPath)
128 {
129     if (!m_initialized) {
130         LogError("Plugins installer not initialized.");
131         return ReturnStatus::NotInitialized;
132     }
133     LogInfo("Plugin installation started. Checking path: " << libPath);
134
135     if (!PluginUtils::checkPath(libPath)) {
136         return ReturnStatus::WrongPluginPath;
137     }
138
139     LogInfo("Plugin path ok. Searching for config file...");
140
141     std::string metaFileName = libPath + DIRECTORY_SEPARATOR +
142         std::string(WrtDB::GlobalConfig::GetPluginMetafileName());
143     if (PluginUtils::checkFileExistance(metaFileName)) {
144         return installPluginFromMetafile(libPath, metaFileName);
145     }
146
147     PluginMetafileData pluginInfo;
148     pluginInfo.m_libraryName = getLibraryName(libPath);
149
150     LogInfo(
151         "Config file done. Lib name: " << pluginInfo.m_libraryName
152                                        <<
153         ". Searching for installed plugin...");
154
155     if (WrtDB::PluginDAO::isPluginInstalled(pluginInfo.m_libraryName)) {
156         LogInfo("Plugin already installed.");
157         return ReturnStatus::AlreadyInstalled;
158     }
159     LogInfo("Plugin not installed. Loading library file...");
160
161     PluginObjectsPtr libraryObjects;
162     PluginHandle pluginHandle;
163
164     std::string filename = libPath + DIRECTORY_SEPARATOR +
165         pluginInfo.m_libraryName;
166
167     LogDebug("Loading plugin: " << filename);
168
169     Plugin* plugin;
170     Try
171     {
172         plugin = m_registry.GetPlugin(filename);
173     }
174     Catch(DPL::Exception) {
175         LogError("Loading library failed " << filename);
176         return ReturnStatus::LoadingLibraryError;
177     }
178     libraryObjects = PluginObjectsPtr(new PluginObjects());
179
180     LogInfo("#####");
181     LogInfo("##### Plugin: " << filename << " supports new plugin API");
182     LogInfo("#####");
183
184     FOREACH(o, *plugin->GetObjects()) {
185         libraryObjects->addObjects(CAST(*o)->GetParentName(),
186                                    CAST(*o)->GetName());
187
188         LogDebug("[Parent << Object] " << CAST(*o)->GetParentName()
189                                        << " << "
190                                        << CAST(*o)->GetName());
191
192         registerObjects(libraryObjects, plugin->GetObjects());
193     }
194
195     if (!fillMappingInterfaces(pluginInfo, filename)) {
196         m_registry.RemovePlugin(filename, *plugin);
197         return ReturnStatus::LoadingLibraryError;
198     }
199
200     LogInfo("Library loaded. Registering plugin...");
201
202     Try
203     {
204         pluginHandle =
205             PluginDAO::registerPlugin(pluginInfo, libPath);
206
207         LogInfo("Plugin registered. Registering features...");
208
209         FOREACH(it, pluginInfo.m_featureContainer)
210         {
211             LogError("PluginHandle: " << pluginHandle);
212             FeatureDAO::RegisterFeature(*it, pluginHandle);
213         }
214
215         LogInfo("Features registered. Registering objects...");
216
217         registerPluginObjects(pluginHandle, libraryObjects);
218
219         LogInfo("Registration done. Resolving dependencies...");
220
221         //TODO: can it be replaced with resolvePluginDependencies(handle)
222         if (!registerAndUpdateInstallation(pluginHandle, libraryObjects)) {
223             return ReturnStatus::InstallationWaiting;
224         }
225     } Catch(DPL::Exception) {
226         LogError("Failed to make database entry.");
227         return ReturnStatus::DatabaseError;
228     }
229
230     LogInfo("Plugin installed successfully.");
231     return ReturnStatus::Success;
232 }
233
234 PluginObjectsPtr PluginsInstaller::loadLibraryFromMetafile(
235     const std::string& libName) const
236 {
237     LogInfo("Loading library: " << libName);
238
239     void *dlHandle = dlopen(libName.c_str(), RTLD_NOW);
240     if (dlHandle == NULL) {
241         LogError(
242             "Failed to load plugin: " << libName <<
243             ". Reason: " << dlerror());
244         ThrowMsg(PluginInstall::Exceptions::LibraryException, "Library error");
245     }
246
247     const js_entity_definition_t *rawEntityList = NULL;
248     get_widget_entity_map_proc *getWidgetEntityMapProcPtr = NULL;
249
250     getWidgetEntityMapProcPtr =
251         reinterpret_cast<get_widget_entity_map_proc *>(dlsym(dlHandle,
252                                                              PLUGIN_GET_CLASS_MAP_PROC_NAME));
253
254     if (getWidgetEntityMapProcPtr) {
255         rawEntityList = (*getWidgetEntityMapProcPtr)();
256     } else {
257         rawEntityList =
258             static_cast<const js_entity_definition_t *>(dlsym(dlHandle,
259                                                               PLUGIN_CLASS_MAP_NAME));
260     }
261
262     if (rawEntityList == NULL) {
263         dlclose(dlHandle);
264         LogError("Failed to read class name" << libName);
265         ThrowMsg(PluginInstall::Exceptions::LibraryException, "Library error");
266     }
267
268     PluginObjectsPtr libraryObjects = PluginObjectsPtr(new PluginObjects());
269     const js_entity_definition_t *rawEntityListIterator = rawEntityList;
270
271     LogInfo("#####");
272     LogInfo("##### Plugin: " << libName << " is using deprecated API");
273     LogInfo("#####");
274
275     while (rawEntityListIterator->parent_name != NULL &&
276            rawEntityListIterator->object_name != NULL)
277     {
278         LogInfo("#####     [" << rawEntityListIterator->object_name << "]: ");
279         LogInfo("#####     Parent: " << rawEntityListIterator->parent_name);
280         LogInfo("#####");
281
282         libraryObjects->addObjects(rawEntityListIterator->parent_name,
283                                    rawEntityListIterator->object_name);
284
285         ++rawEntityListIterator;
286     }
287
288     // Unload library
289     if (dlclose(dlHandle) != 0) {
290         LogError("Cannot close plugin handle");
291     } else {
292         LogDebug("Library is unloaded");
293     }
294
295     // Load export table
296     LogDebug("Library successfuly loaded and parsed");
297
298     return libraryObjects;
299 }
300
301 PluginsInstaller::ReturnStatus PluginsInstaller::installPluginFromMetafile(
302     const std::string& path, const std::string& metaFilePath)
303 {
304     if (!m_initialized) {
305         LogError("Plugins installer not initialized.");
306         return ReturnStatus::NotInitialized;
307     }
308     OptionalPluginMetafileData pluginData;
309     Try
310     {
311         pluginData = parseMetafile(metaFilePath);
312     }
313     Catch(PluginInstall::Exceptions::XMLFileParsingException)
314     {
315         LogError("Parsing metafile failed.");
316         return ReturnStatus::MetafileError;
317     }
318     if (pluginData.IsNull()) {
319         return ReturnStatus::MetafileError;
320     }
321
322     if (WrtDB::PluginDAO::isPluginInstalled(pluginData->m_libraryName)) {
323         LogInfo("Plugin already installed.");
324         return ReturnStatus::AlreadyInstalled;
325     }
326     Try {
327         LogError(
328             "path is: " << path << ", libraryName: " <<
329             pluginData->m_libraryName);
330         PluginObjectsPtr objects = loadLibraryFromMetafile(
331                 path + DIRECTORY_SEPARATOR + pluginData->m_libraryName);
332
333         PluginHandle pluginHandle =
334             PluginDAO::registerPlugin(*pluginData, path);
335
336         LogInfo("Plugin registered. Registering features...");
337
338         FOREACH(it, pluginData->m_featureContainer)
339         {
340             LogError("PluginHandle: " << pluginHandle);
341             FeatureDAO::RegisterFeature(*it, pluginHandle);
342         }
343
344         LogInfo("Features registered. Registering objects...");
345
346         registerPluginObjects(pluginHandle, objects);
347
348         LogInfo("Objects registered. Finishing...");
349
350         if (!registerAndUpdateInstallation(pluginHandle, objects)) {
351             return ReturnStatus::InstallationWaiting;
352         }
353     } Catch(DPL::Exception) {
354         LogError("Failed to make database entry.");
355         return ReturnStatus::DatabaseError;
356     }
357
358     LogInfo("Plugin installed successfully.");
359     return ReturnStatus::Success;
360 }
361
362 int PluginsInstaller::installAllPlugins()
363 {
364     if (!m_initialized) {
365         LogError("Plugins installer not initialized.");
366         return INSTALLATION_ERROR;
367     }
368     LogDebug("Installing plugins ...");
369
370     std::string PLUGIN_PATH = std::string(GlobalConfig::GetDevicePluginPath());
371
372     DIR *dir;
373     dir = opendir(PLUGIN_PATH.c_str());
374
375     if (!dir) {
376         return INSTALLATION_ERROR;
377     }
378
379     LogInfo("Plugin DIRECTORY IS" << PLUGIN_PATH);
380     int return_code;
381     struct dirent libdir;
382     struct dirent* result;
383
384     errno = 0;
385
386     std::list<std::string> pluginsPaths;
387
388     for (return_code = readdir_r(dir, &libdir, &result);
389             result != NULL && return_code == 0;
390             return_code = readdir_r(dir, &libdir, &result)) {
391         if (strcmp(libdir.d_name, ".") == 0 ||
392             strcmp(libdir.d_name, "..") == 0)
393         {
394             continue;
395         }
396
397         std::string path = PLUGIN_PATH;
398         path += "/";
399         path += libdir.d_name;
400
401         struct stat tmp;
402
403         if (stat(path.c_str(), &tmp) == -1) {
404             LogError("Failed to open file" << path);
405             continue;
406         }
407
408         if (!S_ISDIR(tmp.st_mode)) {
409             LogError("Not a directory" << path);
410             continue;
411         }
412
413         LogDebug("Found plugin at " << path);
414         pluginsPaths.push_back(path);
415     }
416
417     if (0 != return_code)
418         LogError("Error while reading directory.");
419
420     if (-1 == TEMP_FAILURE_RETRY(closedir(dir))) {
421         LogError("Failed to close dir: " << PLUGIN_PATH);
422     }
423
424     LogDebug("Plugins to install: " << pluginsPaths.size());
425
426     for (size_t k = 0; k <= pluginsPaths.size(); ++k) {
427         printf(" ");
428     }
429     printf("]\r[");
430     int installedPluginsCount = 0;
431     ReturnStatus ret = ReturnStatus::Unknown;
432     FOREACH(it, pluginsPaths) {
433         LogInfo("Preparing to plugin installation: " << *it);
434         ret = installPlugin(*it);
435         if (ReturnStatus::Success == ret) {
436             ++installedPluginsCount;
437             LogInfo("Plugin " << *it << " installed.");
438         } else if (ReturnStatus::InstallationWaiting == ret) {
439             LogWarning("Plugin not installed. Waiting for dependency");
440         } else {
441             LogError("Plugin installation failed");
442         }
443         printf("#");
444         fflush(stdout);
445     }
446     printf("\n");
447     installedPluginsCount += installWaitingPlugins();
448     m_registry.UnloadAll();
449     LogInfo("Installed " << installedPluginsCount
450                          << " plugins of total: " << pluginsPaths.size());
451     return installedPluginsCount;
452 }
453
454 int PluginsInstaller::installWaitingPlugins()
455 {
456     PluginHandleSetPtr waitingPlugins;
457
458     waitingPlugins =
459         PluginDAO::getPluginHandleByStatus(PluginDAO::INSTALLATION_WAITING);
460
461     int pluginsInstalled = 0;
462     FOREACH(it, *waitingPlugins)
463     {
464         if (resolvePluginDependencies(*it)) {
465             ++pluginsInstalled;
466         }
467     }
468     return pluginsInstalled;
469 }
470
471 bool PluginsInstaller::resolvePluginDependencies(PluginHandle handle)
472 {
473     PluginHandleSetPtr dependencies(new PluginHandleSet);
474
475     PluginObjects::ObjectsPtr requiredObjects =
476         PluginDAO::getRequiredObjectsForPluginHandle(handle);
477
478     PluginHandle depHandle = INVALID_PLUGIN_HANDLE;
479
480     FOREACH(requiredObject, *requiredObjects)
481     {
482         depHandle =
483             PluginDAO::getPluginHandleForImplementedObject(*requiredObject);
484
485         if (INVALID_PLUGIN_HANDLE == depHandle) {
486             LogError("Library implementing: " <<
487                      *requiredObject << " NOT FOUND");
488             return false;
489         }
490         dependencies->insert(depHandle);
491     }
492
493     PluginDAO::registerPluginLibrariesDependencies(handle, dependencies);
494     PluginDAO::setPluginInstallationStatus(handle,
495                                            PluginDAO::INSTALLATION_COMPLETED);
496
497     return true;
498 }
499
500 void PluginsInstaller::registerObjects(const PluginObjectsPtr& libObj,
501                                        const IObjectsListPtr& objects) const
502 {
503     LogDebug("registerObjects invoked");
504
505     FOREACH(o, *objects)
506     {
507         auto children = CAST(*o)->GetChildren();
508
509         if (children) {
510             FOREACH(c, *children)
511             {
512                 libObj->addObjects(CAST(*o)->GetName(), CAST(*c)->GetName());
513
514                 LogDebug("[Parent << Object] " << CAST(*c)->GetName()
515                                                << " << "
516                                                << CAST(*o)->GetName());
517             }
518
519             registerObjects(libObj, children);
520         }
521     }
522 }
523
524 PluginsInstaller::OptionalPluginMetafileData PluginsInstaller::parseMetafile(
525     const std::string& path) const
526 {
527     LogInfo("Plugin Config file::" << path);
528     Try
529     {
530         PluginMetafileData pluginInfo;
531         PluginMetafileReader reader;
532         reader.initialize(path);
533         reader.read(pluginInfo);
534
535         FOREACH(it, pluginInfo.m_featureContainer) {
536             LogDebug("Parsed feature : " << it->m_name);
537
538             FOREACH(devCap, it->m_deviceCapabilities) {
539                 LogDebug("  |  DevCap : " << *devCap);
540             }
541         }
542         return OptionalPluginMetafileData(pluginInfo);
543     }
544     Catch(ValidationCore::ParserSchemaException::Base) {
545         LogError("Error during file processing " << path);
546         ThrowMsg(PluginInstall::Exceptions::XMLFileParsingException,
547                  "Parsing metafile failed");
548     }
549 }
550
551 std::string PluginsInstaller::getLibraryName(const std::string& dirPath) const
552 {
553     std::string pluginPath = dirPath;
554     size_t indexpos = pluginPath.find_last_of('/');
555
556     if (std::string::npos == indexpos) {
557         indexpos = 0;
558     } else {
559         indexpos += 1;  // move after '/'
560     }
561
562     std::string libName = pluginPath.substr(indexpos);
563     libName = WrtDB::GlobalConfig::GetPluginPrefix() + libName
564         + WrtDB::GlobalConfig::GetPluginSuffix();
565     LogDebug("Plugin .so: " << libName);
566     return libName;
567 }
568
569 bool PluginsInstaller::registerAndUpdateInstallation(
570     const WrtDB::DbPluginHandle& pluginHandle,
571     const PluginObjectsPtr& libraries)
572 {
573     PluginHandleSetPtr handles = PluginHandleSetPtr(new PluginHandleSet);
574
575     DbPluginHandle handle = INVALID_PLUGIN_HANDLE;
576
577     //register requiredObjects
578     FOREACH(it, *(libraries->getDependentObjects()))
579     {
580         if (libraries->hasObject(*it)) {
581             LogDebug("Dependency from the same library. ignored");
582             continue;
583         }
584
585         handle = PluginDAO::getPluginHandleForImplementedObject(*it);
586         if (handle == INVALID_PLUGIN_HANDLE) {
587             LogError("Library implementing: " << *it << " NOT FOUND");
588             PluginDAO::setPluginInstallationStatus(
589                 pluginHandle,
590                 PluginDAO::INSTALLATION_WAITING);
591             return false;
592         }
593
594         handles->insert(handle);
595     }
596
597     PluginDAO::registerPluginLibrariesDependencies(pluginHandle, handles);
598
599     PluginDAO::setPluginInstallationStatus(pluginHandle,
600                                            PluginDAO::INSTALLATION_COMPLETED);
601     return true;
602 }
603
604 bool PluginsInstaller::fillMappingInterfaces(PluginMetafileData& pluginData,
605                                              const std::string& filename)
606 {
607     void *dlHandle = dlopen(filename.c_str(), RTLD_NOW);
608     if (dlHandle == NULL) {
609         LogError(
610             "Failed to load plugin: " << filename << ". Reason: " << dlerror());
611         return false;
612     }
613     Try
614     {
615         ExportedApi* entryPoint =
616             static_cast<ExportedApi*>(dlsym(dlHandle, GetExportedSymbolName()));
617         if (NULL == entryPoint) {
618             LogError("Error: " << dlerror());
619             ThrowMsg(PluginInstall::Exceptions::LibraryException,
620                      "Library error");
621         }
622
623         // obtain feature -> dev-cap mapping
624         feature_mapping_interface_t mappingInterface = { NULL, NULL, NULL };
625         entryPoint->GetProvidedFeatures(&mappingInterface);
626
627         if (!mappingInterface.featGetter || !mappingInterface.release ||
628             !mappingInterface.dcGetter)
629         {
630             LogError("Failed to obtain mapping interface from .so");
631             ThrowMsg(PluginInstall::Exceptions::LibraryException,
632                      "Library error");
633         }
634
635         feature_mapping_t* devcapMapping = mappingInterface.featGetter();
636
637         LogDebug("Getting mapping from features to device capabilities");
638
639         for (size_t i = 0; i < devcapMapping->featuresCount; ++i) {
640             PluginMetafileData::Feature feature;
641             feature.m_name = devcapMapping->features[i].feature_name;
642
643             LogDebug("Feature: " << feature.m_name);
644
645             const devcaps_t* dc =
646                 mappingInterface.dcGetter(
647                     devcapMapping,
648                     devcapMapping->features[i].
649                         feature_name);
650
651             LogDebug("device=cap: " << dc);
652
653             if (dc) {
654                 LogDebug("devcaps count: " << dc->devCapsCount);
655
656                 for (size_t j = 0; j < dc->devCapsCount; ++j) {
657                     LogDebug("devcap: " << dc->deviceCaps[j]);
658                     feature.m_deviceCapabilities.insert(dc->deviceCaps[j]);
659                 }
660             }
661
662             pluginData.m_featureContainer.insert(feature);
663         }
664
665         mappingInterface.release(devcapMapping);
666     } Catch(PluginInstall::Exceptions::PluginsInstallerException)
667     {
668         LogError("Exception while feature mapping");
669         dlclose(dlHandle);
670         return false;
671     }
672
673     // Unload library
674     if (dlclose(dlHandle) != 0) {
675         LogError("Cannot close plugin handle");
676     } else {
677         LogDebug("Library is unloaded");
678     }
679     return true;
680 }
681
682 void PluginsInstaller::registerPluginObjects(
683     const WrtDB::DbPluginHandle& handle,
684     const PluginObjectsPtr libObjects)
685 const
686 {
687     //register implemented objects
688     PluginObjects::ObjectsPtr objects =
689         libObjects->getImplementedObject();
690
691     FOREACH(it, *objects)
692     {
693         WrtDB::PluginDAO::registerPluginImplementedObject(*it, handle);
694     }
695
696     //register requiredObjects
697     objects = libObjects->getDependentObjects();
698
699     FOREACH(it, *objects)
700     {
701         if (libObjects->hasObject(*it)) {
702             LogDebug("Dependency from the same library. ignored");
703             continue;
704         }
705         WrtDB::PluginDAO::registerPluginRequiredObject(*it, handle);
706     }
707 }
708