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