Tizen 2.1 base
[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     LogInfo("Plugin path ok. Searching for config file...");
139
140     std::string metaFileName = libPath + DIRECTORY_SEPARATOR +
141         std::string(WrtDB::GlobalConfig::GetPluginMetafileName());
142     if (PluginUtils::checkFileExistance(metaFileName))
143     {
144         return installPluginFromMetafile(libPath, metaFileName);
145     }
146
147     PluginMetafileData pluginInfo;
148     pluginInfo.m_libraryName = getLibraryName(libPath);
149
150     LogInfo("Config file done. Lib name: " << pluginInfo.m_libraryName
151             << ". Searching for installed plugin...");
152
153     if (WrtDB::PluginDAO::isPluginInstalled(pluginInfo.m_libraryName)) {
154         LogInfo("Plugin already installed.");
155         return ReturnStatus::AlreadyInstalled;
156     }
157     LogInfo("Plugin not installed. Loading library file...");
158
159     PluginObjectsPtr libraryObjects;
160     PluginHandle pluginHandle;
161
162     std::string filename = libPath + DIRECTORY_SEPARATOR +
163         pluginInfo.m_libraryName;
164
165     LogDebug("Loading plugin: " << filename);
166
167     Plugin* plugin;
168     Try
169     {
170         plugin = m_registry.GetPlugin(filename);
171     }
172     Catch(DPL::Exception) {
173         LogError("Loading library failed " << filename);
174         return ReturnStatus::LoadingLibraryError;
175     }
176     libraryObjects = PluginObjectsPtr(new PluginObjects());
177
178     LogInfo("#####");
179     LogInfo("##### Plugin: " << filename << " supports new plugin API");
180     LogInfo("#####");
181
182     FOREACH(o, *plugin->GetObjects()) {
183         libraryObjects->addObjects(CAST(*o)->GetParentName(),
184             CAST(*o)->GetName());
185
186         LogDebug("[Parent << Object] " << CAST(*o)->GetParentName()
187             << " << "
188             << CAST(*o)->GetName());
189
190         registerObjects(libraryObjects, plugin->GetObjects());
191     }
192
193     if (!fillMappingInterfaces(pluginInfo, filename)) {
194         m_registry.RemovePlugin(filename, *plugin);
195         return ReturnStatus::LoadingLibraryError;
196     }
197
198     LogInfo("Library loaded. Registering plugin...");
199
200     Try
201     {
202         pluginHandle =
203             PluginDAO::registerPlugin(pluginInfo, libPath);
204
205         LogInfo("Plugin registered. Registering features...");
206
207         FOREACH(it, pluginInfo.m_featureContainer)
208         {
209             LogError("PluginHandle: " << pluginHandle);
210             FeatureDAO::RegisterFeature(*it, pluginHandle);
211         }
212
213         LogInfo("Features registered. Registering objects...");
214
215         registerPluginObjects(pluginHandle, libraryObjects);
216
217         LogInfo("Registration done. Resolving dependencies...");
218
219         //TODO: can it be replaced with resolvePluginDependencies(handle)
220         if (!registerAndUpdateInstallation(pluginHandle, libraryObjects))
221             return ReturnStatus::InstallationWaiting;
222     } Catch (DPL::Exception) {
223         LogError("Failed to make database entry.");
224         return ReturnStatus::DatabaseError;
225     }
226
227     LogInfo("Plugin installed successfully.");
228     return ReturnStatus::Success;
229 }
230
231 PluginObjectsPtr PluginsInstaller::loadLibraryFromMetafile(
232         const std::string& libName) const
233 {
234     LogInfo("Loading library: " << libName);
235
236     void *dlHandle = dlopen(libName.c_str(), RTLD_NOW);
237     if (dlHandle == NULL ) {
238         LogError(
239                 "Failed to load plugin: " << libName <<
240                 ". Reason: " << dlerror());
241         ThrowMsg(PluginInstall::Exceptions::LibraryException, "Library error");
242     }
243
244     const js_entity_definition_t *rawEntityList = NULL;
245     get_widget_entity_map_proc *getWidgetEntityMapProcPtr = NULL;
246
247     getWidgetEntityMapProcPtr =
248         reinterpret_cast<get_widget_entity_map_proc *>(dlsym(dlHandle,
249                     PLUGIN_GET_CLASS_MAP_PROC_NAME));
250
251     if (getWidgetEntityMapProcPtr) {
252         rawEntityList = (*getWidgetEntityMapProcPtr)();
253     } else {
254         rawEntityList =
255             static_cast<const js_entity_definition_t *>(dlsym(dlHandle,
256                         PLUGIN_CLASS_MAP_NAME));
257     }
258
259     if (rawEntityList == NULL) {
260         dlclose(dlHandle);
261         LogError("Failed to read class name" << libName);
262         ThrowMsg(PluginInstall::Exceptions::LibraryException, "Library error");
263     }
264
265     PluginObjectsPtr libraryObjects = PluginObjectsPtr(new PluginObjects());
266     const js_entity_definition_t *rawEntityListIterator = rawEntityList;
267
268     LogInfo("#####");
269     LogInfo("##### Plugin: " << libName << " is using deprecated API");
270     LogInfo("#####");
271
272     while (rawEntityListIterator->parent_name != NULL &&
273             rawEntityListIterator->object_name != NULL)
274     {
275         LogInfo("#####     [" << rawEntityListIterator->object_name << "]: ");
276         LogInfo("#####     Parent: " << rawEntityListIterator->parent_name);
277         LogInfo("#####");
278
279         libraryObjects->addObjects(rawEntityListIterator->parent_name,
280                 rawEntityListIterator->object_name);
281
282         ++rawEntityListIterator;
283     }
284
285     // Unload library
286     if (dlclose(dlHandle) != 0) {
287         LogError("Cannot close plugin handle");
288     } else {
289         LogDebug("Library is unloaded");
290     }
291
292     // Load export table
293     LogDebug("Library successfuly loaded and parsed");
294
295     return libraryObjects;
296 }
297
298
299 PluginsInstaller::ReturnStatus PluginsInstaller::installPluginFromMetafile(
300         const std::string& path, const std::string& metaFilePath)
301 {
302     if (!m_initialized) {
303         LogError("Plugins installer not initialized.");
304         return ReturnStatus::NotInitialized;
305     }
306     OptionalPluginMetafileData pluginData;
307     Try
308     {
309         pluginData = parseMetafile(metaFilePath);
310     }
311     Catch (PluginInstall::Exceptions::XMLFileParsingException)
312     {
313         LogError("Parsing metafile failed.");
314         return ReturnStatus::MetafileError;
315     }
316     if (pluginData.IsNull()) {
317         return ReturnStatus::MetafileError;
318     }
319
320     if (WrtDB::PluginDAO::isPluginInstalled(pluginData->m_libraryName)) {
321         LogInfo("Plugin already installed.");
322         return ReturnStatus::AlreadyInstalled;
323     }
324     Try {
325
326         LogError("path is: " << path << ", libraryName: " << pluginData->m_libraryName);
327         PluginObjectsPtr objects = loadLibraryFromMetafile(
328             path + DIRECTORY_SEPARATOR + pluginData->m_libraryName);
329
330         PluginHandle pluginHandle =
331             PluginDAO::registerPlugin(*pluginData, path);
332
333         LogInfo("Plugin registered. Registering features...");
334
335         FOREACH(it, pluginData->m_featureContainer)
336         {
337             LogError("PluginHandle: " << pluginHandle);
338             FeatureDAO::RegisterFeature(*it, pluginHandle);
339         }
340
341         LogInfo("Features registered. Registering objects...");
342
343         registerPluginObjects(pluginHandle, objects);
344
345         LogInfo("Objects registered. Finishing...");
346
347         if (!registerAndUpdateInstallation(pluginHandle, objects))
348             return ReturnStatus::InstallationWaiting;
349     } Catch (DPL::Exception) {
350         LogError("Failed to make database entry.");
351         return ReturnStatus::DatabaseError;
352     }
353
354     LogInfo("Plugin installed successfully.");
355     return ReturnStatus::Success;
356 }
357
358 int PluginsInstaller::installAllPlugins()
359 {
360     if (!m_initialized) {
361         LogError("Plugins installer not initialized.");
362         return INSTALLATION_ERROR;
363     }
364     LogDebug("Installing plugins ...");
365
366     std::string PLUGIN_PATH = std::string(GlobalConfig::GetDevicePluginPath());
367
368     DIR *dir;
369     dir = opendir(PLUGIN_PATH.c_str());
370
371     if (!dir) {
372         return INSTALLATION_ERROR;
373     }
374
375     LogInfo("Plugin DIRECTORY IS" << PLUGIN_PATH);
376     struct dirent* libdir;
377
378     errno = 0;
379
380     std::list<std::string> pluginsPaths;
381
382     while ((libdir = readdir(dir)) != 0) {
383         if (strcmp(libdir->d_name, ".") == 0 ||
384             strcmp(libdir->d_name, "..") == 0) {
385             continue;
386         }
387
388         std::string path = PLUGIN_PATH;
389         path += "/";
390         path += libdir->d_name;
391
392         struct stat tmp;
393
394         if (stat(path.c_str(), &tmp) == -1) {
395             LogError("Failed to open file" << path);
396             continue;
397         }
398
399         if (!S_ISDIR(tmp.st_mode)) {
400             LogError("Not a directory" << path);
401             continue;
402         }
403
404         LogDebug("Found plugin at " << path);
405         pluginsPaths.push_back(path);
406     }
407
408     if (-1 == TEMP_FAILURE_RETRY(closedir(dir))) {
409         LogError("Failed to close dir: " << PLUGIN_PATH);
410     }
411
412     LogDebug("Plugins to install: " << pluginsPaths.size());
413
414     for (int k = 0; k <= pluginsPaths.size(); ++k)
415         printf(" ");
416     printf("]\r[");
417     int installedPluginsCount = 0;
418     ReturnStatus ret = ReturnStatus::Unknown;
419     FOREACH(it, pluginsPaths) {
420         LogInfo("Preparing to plugin installation: " << *it);
421         ret = installPlugin(*it);
422         if (ReturnStatus::Success == ret) {
423             ++installedPluginsCount;
424             LogInfo("Plugin " << *it << " installed.");
425         } else if (ReturnStatus::InstallationWaiting == ret) {
426             LogWarning("Plugin not installed. Waiting for dependency");
427         } else {
428             LogError("Plugin installation failed");
429         }
430         printf("#");
431         fflush(stdout);
432     }
433     printf("\n");
434     installedPluginsCount += installWaitingPlugins();
435     m_registry.UnloadAll();
436     LogInfo("Installed " << installedPluginsCount
437             << " plugins of total: " << pluginsPaths.size());
438     return installedPluginsCount;
439 }
440
441 int PluginsInstaller::installWaitingPlugins()
442 {
443     PluginHandleSetPtr waitingPlugins;
444
445     waitingPlugins =
446         PluginDAO::getPluginHandleByStatus(PluginDAO::INSTALLATION_WAITING);
447
448     int pluginsInstalled = 0;
449     FOREACH(it, *waitingPlugins)
450     {
451         if (resolvePluginDependencies(*it))
452             ++pluginsInstalled;
453     }
454     return pluginsInstalled;
455 }
456
457 bool PluginsInstaller::resolvePluginDependencies(PluginHandle handle)
458 {
459     PluginHandleSetPtr dependencies(new PluginHandleSet);
460
461     PluginObjects::ObjectsPtr requiredObjects =
462         PluginDAO::getRequiredObjectsForPluginHandle(handle);
463
464     PluginHandle depHandle = INVALID_PLUGIN_HANDLE;
465
466     FOREACH(requiredObject, *requiredObjects)
467     {
468         depHandle =
469             PluginDAO::getPluginHandleForImplementedObject(*requiredObject);
470
471         if (INVALID_PLUGIN_HANDLE == depHandle) {
472             LogError("Library implementing: " <<
473                      *requiredObject << " NOT FOUND");
474             return false;
475         }
476         dependencies->insert(depHandle);
477     }
478
479     PluginDAO::registerPluginLibrariesDependencies(handle, dependencies);
480     PluginDAO::setPluginInstallationStatus(handle,
481                                            PluginDAO::INSTALLATION_COMPLETED);
482
483     return true;
484 }
485
486 void PluginsInstaller::registerObjects(const PluginObjectsPtr& libObj,
487                                        const IObjectsListPtr& objects) const
488 {
489    LogDebug("registerObjects invoked");
490
491    FOREACH(o, *objects)
492    {
493        auto children = CAST(*o)->GetChildren();
494
495        if(children)
496        {
497            FOREACH(c, *children)
498            {
499                libObj->addObjects(CAST(*o)->GetName(), CAST(*c)->GetName());
500
501                LogDebug("[Parent << Object] " << CAST(*c)->GetName()
502                         << " << "
503                         << CAST(*o)->GetName());
504            }
505
506            registerObjects(libObj, children);
507        }
508    }
509 }
510
511
512 PluginsInstaller::OptionalPluginMetafileData PluginsInstaller::parseMetafile(
513         const std::string& path) const
514 {
515     LogInfo("Plugin Config file::" << path);
516     Try
517     {
518         PluginMetafileData pluginInfo;
519         PluginMetafileReader reader;
520         reader.initialize(path);
521         reader.read(pluginInfo);
522
523         FOREACH(it, pluginInfo.m_featureContainer) {
524             LogDebug("Parsed feature : " << it->m_name);
525
526             FOREACH(devCap, it->m_deviceCapabilities) {
527                 LogDebug("  |  DevCap : " << *devCap);
528             }
529         }
530         return OptionalPluginMetafileData(pluginInfo);
531     }
532     Catch(ValidationCore::ParserSchemaException::Base) {
533         LogError("Error during file processing " << path);
534         ThrowMsg(PluginInstall::Exceptions::XMLFileParsingException,
535             "Parsing metafile failed");
536     }
537 }
538
539 std::string PluginsInstaller::getLibraryName(const std::string& dirPath) const
540 {
541     std::string pluginPath = dirPath;
542     size_t indexpos = pluginPath.find_last_of('/');
543
544     if (std::string::npos == indexpos)
545     {
546         indexpos = 0;
547     }
548     else
549     {
550         indexpos += 1;  // move after '/'
551     }
552
553     std::string libName = pluginPath.substr(indexpos);
554     libName = WrtDB::GlobalConfig::GetPluginPrefix() + libName
555         + WrtDB::GlobalConfig::GetPluginSuffix();
556     LogDebug("Plugin .so: " << libName);
557     return libName;
558 }
559
560 bool PluginsInstaller::registerAndUpdateInstallation(
561         const WrtDB::DbPluginHandle& pluginHandle,
562         const PluginObjectsPtr& libraries)
563 {
564     PluginHandleSetPtr handles = PluginHandleSetPtr(new PluginHandleSet);
565
566     DbPluginHandle handle = INVALID_PLUGIN_HANDLE;
567
568     //register requiredObjects
569     FOREACH(it, *(libraries->getDependentObjects()))
570     {
571         if (libraries->hasObject(*it)) {
572             LogDebug("Dependency from the same library. ignored");
573             continue;
574         }
575
576         handle = PluginDAO::getPluginHandleForImplementedObject(*it);
577         if (handle == INVALID_PLUGIN_HANDLE) {
578             LogError("Library implementing: " << *it << " NOT FOUND");
579             PluginDAO::setPluginInstallationStatus(
580                 pluginHandle,
581                 PluginDAO::INSTALLATION_WAITING);
582             return false;
583         }
584
585         handles->insert(handle);
586     }
587
588     PluginDAO::registerPluginLibrariesDependencies(pluginHandle, handles);
589
590     PluginDAO::setPluginInstallationStatus(pluginHandle,
591                                            PluginDAO::INSTALLATION_COMPLETED);
592     return true;
593 }
594
595 bool PluginsInstaller::fillMappingInterfaces(PluginMetafileData& pluginData,
596                                              const std::string& filename)
597 {
598     void *dlHandle = dlopen(filename.c_str(), RTLD_NOW);
599     if (dlHandle == NULL) {
600         LogError("Failed to load plugin: " << filename << ". Reason: " << dlerror());
601         return false;
602     }
603     Try
604     {
605         ExportedApi* entryPoint =
606             static_cast<ExportedApi*> (dlsym(dlHandle, GetExportedSymbolName()));
607         if (NULL == entryPoint) {
608             LogError("Error: " << dlerror());
609             ThrowMsg(PluginInstall::Exceptions::LibraryException, "Library error");
610         }
611
612         // obtain feature -> dev-cap mapping
613         feature_mapping_interface_t mappingInterface = {NULL, NULL, NULL};
614         entryPoint->GetProvidedFeatures(&mappingInterface);
615
616         if (!mappingInterface.featGetter || !mappingInterface.release ||
617             !mappingInterface.dcGetter) {
618             LogError("Failed to obtain mapping interface from .so");
619             ThrowMsg(PluginInstall::Exceptions::LibraryException, "Library error");
620         }
621
622         feature_mapping_t* devcapMapping = mappingInterface.featGetter();
623
624         LogDebug("Getting mapping from features to device capabilities");
625
626         for (size_t i = 0; i < devcapMapping->featuresCount; ++i) {
627             PluginMetafileData::Feature feature;
628             feature.m_name = devcapMapping->features[i].feature_name;
629
630             LogDebug("Feature: " << feature.m_name);
631
632             const devcaps_t* dc =
633                 mappingInterface.dcGetter(devcapMapping,
634                 devcapMapping->features[i].feature_name);
635
636             LogDebug("device=cap: " << dc);
637
638             if (dc) {
639                 LogDebug("devcaps count: " << dc->devCapsCount);
640
641                 for (size_t j = 0; j < dc->devCapsCount; ++j) {
642                     LogDebug("devcap: " << dc->deviceCaps[j]);
643                     feature.m_deviceCapabilities.insert(dc->deviceCaps[j]);
644                 }
645             }
646
647             pluginData.m_featureContainer.insert(feature);
648         }
649
650         mappingInterface.release(devcapMapping);
651     } Catch (PluginInstall::Exceptions::PluginsInstallerException)
652     {
653         LogError("Exception while feature mapping");
654         dlclose(dlHandle);
655         return false;
656     }
657
658     // Unload library
659     if (dlclose(dlHandle) != 0) {
660         LogError("Cannot close plugin handle");
661     } else {
662         LogDebug("Library is unloaded");
663     }
664     return true;
665 }
666
667 void PluginsInstaller::registerPluginObjects(const WrtDB::DbPluginHandle& handle,
668                                              const PluginObjectsPtr libObjects) const
669 {
670         //register implemented objects
671     PluginObjects::ObjectsPtr objects =
672         libObjects->getImplementedObject();
673
674     FOREACH(it, *objects)
675     {
676         WrtDB::PluginDAO::registerPluginImplementedObject(*it, handle);
677     }
678
679     //register requiredObjects
680     objects = libObjects->getDependentObjects();
681
682     FOREACH(it, *objects)
683     {
684         if (libObjects->hasObject(*it)) {
685             LogDebug("Dependency from the same library. ignored");
686             continue;
687         }
688         WrtDB::PluginDAO::registerPluginRequiredObject(*it, handle);
689     }
690 }
691