Fix plugin manager
[platform/core/appfw/app-installers.git] / src / common / plugins / plugin_manager.cc
1 // Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by a apache 2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/plugins/plugin_manager.h"
6
7 #include <pkgmgr-info.h>
8
9 #include <boost/filesystem.hpp>
10
11 #include <algorithm>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "common/plugins/plugin_factory.h"
17 #include "common/plugins/plugin_list_parser.h"
18 #include "common/plugins/plugin_xml_parser.h"
19 #include "common/plugins/category_plugin.h"
20 #include "common/plugins/metadata_plugin.h"
21 #include "common/plugins/tag_plugin.h"
22 #include "common/utils/glist_range.h"
23
24 namespace {
25
26 bool IsSubKey(const std::string& subkey, const std::string& key) {
27   if (subkey.find(key) != 0)
28     return false;
29   if (subkey.size() != key.size() && subkey.at(key.size()) != '/')
30     return false;
31   return true;
32 }
33
34 bool AddPluginInfo(std::shared_ptr<common_installer::PluginInfo> plugin_info,
35                    manifest_x* manifest) {
36   plugin_x *plugin = static_cast<plugin_x*>(calloc(1, sizeof(plugin_x)));
37   if (!plugin) {
38     LOG(ERROR) << "out of memory";
39     return false;
40   }
41   plugin->pkgid = strdup(manifest->package);
42   plugin->plugin_type = strdup(plugin_info->type().c_str());
43   plugin->plugin_name = strdup(plugin_info->name().c_str());
44   if (!plugin->pkgid || !plugin->plugin_type || !plugin->plugin_name) {
45     LOG(ERROR) << "out of memory";
46     free(plugin->pkgid);
47     free(plugin->plugin_type);
48     free(plugin->plugin_name);
49     free(plugin);
50     return false;
51   }
52
53   manifest->plugin = g_list_append(manifest->plugin, plugin);
54   return true;
55 }
56
57 }  // namespace
58
59 namespace common_installer {
60
61 bool PluginManager::IsDataRemoved(const char* data_type,
62                                   const std::string& data) {
63   PkgQueryInterface pkg_query(pkgid_, uid_);
64   return pkg_query.IsPluginExecuted(data_type, data);
65 }
66
67 bool PluginManager::GenerateUnknownTagList(
68     std::vector<std::string>* xml_tags) {
69   if (!xml_parser_.Parse()) {
70     LOG(ERROR) << "Parse xml function error";
71     return false;
72   }
73
74   *xml_tags = xml_parser_.tags_list();
75   return true;
76 }
77
78 bool PluginManager::GeneratePluginInfoList(
79     PluginManager::PluginInfoList* plugin_info_list) {
80   if (!list_parser_.Parse()) {
81     LOG(ERROR) << "Parse list function error";
82     return false;
83   }
84
85   *plugin_info_list = list_parser_.PluginInfoList();
86   return true;
87 }
88
89 bool PluginManager::LoadPlugins(Plugin::ActionType action_type) {
90   std::vector<std::string> xml_tags;
91   if (!GenerateUnknownTagList(&xml_tags))
92     return false;
93
94   PluginInfoList plugin_info_list;
95   if (!GeneratePluginInfoList(&plugin_info_list))
96     return false;
97
98   PluginFactory factory;
99
100   std::sort(xml_tags.begin(), xml_tags.end());
101
102   // This loop loads plugin which are needed according to manifest file
103   // Different pkgmgr plugin types have different condition upon which they
104   // are being loaded
105   LOG(DEBUG) << "Loading pkgmgr plugins...";
106   for (std::shared_ptr<PluginInfo> plugin_info : plugin_info_list) {
107     std::unique_ptr<Plugin> plugin;
108     if (plugin_info->type() == TagPlugin::kType) {
109       // load tag plugin only if tag exists in manifest file
110       auto iter = std::lower_bound(xml_tags.begin(), xml_tags.end(),
111                                      plugin_info->name());
112       if (iter != xml_tags.end() && *iter == plugin_info->name()) {
113         plugin = factory.CreatePluginByPluginInfo(*plugin_info);
114         if (!plugin) {
115           LOG(WARNING) << "Failed to load plugin: " << plugin_info->path()
116                      << " Plugin has been skipped.";
117         } else {
118           if (!AddPluginInfo(plugin_info, manifest_))
119             return false;
120         }
121       } else if (action_type == Plugin::ActionType::Upgrade) {
122         if (IsDataRemoved(TagPlugin::kType, plugin_info->name())) {
123           plugin = factory.CreatePluginByPluginInfo(*plugin_info,
124               Plugin::ActionType::Removed);
125           if (!plugin)
126             LOG(WARNING) << "Failed to load plugin: " << plugin_info->path()
127                          << " Plugin has been skipped.";
128         }
129       }
130     } else if (plugin_info->type() == MetadataPlugin::kType) {
131       bool done = false;
132       for (application_x* app : GListRange<application_x*>(
133            manifest_->application)) {
134         for (metadata_x* meta : GListRange<metadata_x*>(app->metadata)) {
135           if (IsSubKey(meta->key, plugin_info->name())) {
136             plugin = factory.CreatePluginByPluginInfo(*plugin_info);
137             if (!plugin)
138               LOG(WARNING) << "Failed to load plugin: "
139                            << plugin_info->path()
140                            << " Plugin has been skipped.";
141             done = true;
142             break;
143           }
144         }
145         if (done)
146           break;
147       }
148       /* metadata of package, since Tizen 7.5 */
149       for (metadata_x* meta : GListRange<metadata_x*>(manifest_->metadata)) {
150         if (IsSubKey(meta->key, plugin_info->name())) {
151           plugin = factory.CreatePluginByPluginInfo(*plugin_info);
152           if (!plugin)
153             LOG(WARNING) << "Failed to load plugin: "
154                          << plugin_info->path()
155                          << " Plugin has been skipped.";
156           break;
157         }
158       }
159       if (!done && action_type == Plugin::ActionType::Upgrade &&
160           IsDataRemoved(MetadataPlugin::kType, plugin_info->name())) {
161         plugin = factory.CreatePluginByPluginInfo(*plugin_info,
162             Plugin::ActionType::Removed);
163         if (!plugin)
164           LOG(WARNING) << "Failed to load plugin: "
165                        << plugin_info->path()
166                        << " Plugin has been skipped.";
167       }
168     } else if (plugin_info->type() == CategoryPlugin::kType) {
169       bool done = false;
170       for (application_x* app : GListRange<application_x*>(
171            manifest_->application)) {
172         for (const char* category : GListRange<char*>(app->category)) {
173           if (std::string(category).compare(plugin_info->name()) == 0) {
174             plugin = factory.CreatePluginByPluginInfo(*plugin_info);
175             if (!plugin) {
176               LOG(WARNING) << "Failed to load plugin: "
177                            << plugin_info->path()
178                            << " Plugin has been skipped.";
179             }
180             done = true;
181             break;
182           }
183         }
184         if (done)
185           break;
186       }
187       if (!done && action_type == Plugin::ActionType::Upgrade &&
188           IsDataRemoved(CategoryPlugin::kType, plugin_info->name())) {
189         plugin = factory.CreatePluginByPluginInfo(*plugin_info,
190             Plugin::ActionType::Removed);
191         if (!plugin)
192           LOG(WARNING) << "Failed to load plugin: "
193                        << plugin_info->path()
194                        << " Plugin has been skipped.";
195       }
196     }
197
198     if (plugin) {
199       loaded_plugins_.push_back(std::move(plugin));
200       LOG(DEBUG) << "Loaded plugin: " << plugin_info->path();
201     }
202   }
203   return true;
204 }
205
206 bool PluginManager::RunPlugins(Plugin::ActionType action_type) {
207   LOG(DEBUG) << "Running pkgmgr plugins...";
208   for (auto& plugin : loaded_plugins_) {
209     auto& plugin_info = plugin->plugin_info();
210     LOG(INFO) << "Running plugin: " << plugin_info.path();
211     bool success = plugin->Run(xml_parser_.doc_ptr(), manifest_, action_type);
212     if (!success) {
213       bool vitalness = plugin_info.vitalness();
214       if (vitalness) {
215         LOG(ERROR) << plugin_info.path() << " fails";
216         return false;
217       } else {
218         LOG(ERROR) << plugin_info.path()
219                    << " fails but installation is ongoing";
220       }
221     }
222   }
223   return true;
224 }
225
226 }  // namespace common_installer