Create cache on a separate thread
[platform/core/appfw/pkgmgr-info.git] / src / server / database / db_handle_provider.cc
1 /*
2  * Copyright (c) 2021 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 #include "db_handle_provider.hh"
18
19 #include <fcntl.h>
20 #include <glib.h>
21 #include <sys/types.h>
22
23 #include <tzplatform_config.h>
24
25 #include <algorithm>
26 #include <string>
27 #include <vector>
28
29 #include "cache_flag.hh"
30 #include "utils/logging.hh"
31
32 #include "pkgmgr-info.h"
33 #include "pkgmgrinfo_debug.h"
34 #include "pkgmgrinfo_internal.h"
35 #include "pkgmgrinfo_private.h"
36
37 #ifdef LOG_TAG
38 #undef LOG_TAG
39 #endif
40 #define LOG_TAG "PKGMGR_INFO"
41
42 namespace {
43
44 uid_t globaluser_uid = -1;
45
46 uid_t GetGlobalUID() {
47   if (globaluser_uid == (uid_t)-1)
48     globaluser_uid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
49
50   return globaluser_uid;
51 }
52
53 uid_t ConvertUID(uid_t uid) {
54   if (uid < REGULAR_USER)
55     return GetGlobalUID();
56   else
57     return uid;
58 }
59
60 static const std::string global_parser_memory_db_path =
61     "file:parserdb?mode=memory&cache=shared";
62
63 static const std::string cert_memory_db_path =
64     "file:certdb?mode=memory&cache=shared";
65
66 }  // namespace
67
68 namespace pkgmgr_server {
69 namespace database {
70
71 std::unordered_map<uid_t, std::unique_ptr<DBHandleProvider>>
72     DBHandleProvider::provider_;
73 bool DBHandleProvider::is_memory_global_ = false;
74 std::unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
75     DBHandleProvider::global_parser_memory_db_handle_(nullptr,
76                                                       sqlite3_close_v2);
77 std::unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
78     DBHandleProvider::cert_memory_db_handle_(nullptr, sqlite3_close_v2);
79 std::string DBHandleProvider::global_parser_file_db_path_;
80 std::string DBHandleProvider::cert_file_db_path_;
81 std::unordered_set<pid_t> DBHandleProvider::pid_list_;
82 std::recursive_mutex DBHandleProvider::lock_;
83 std::mutex DBHandleProvider::pid_list_lock_;
84
85 DBHandleProvider::DBHandleProvider(uid_t uid)
86     : uid_(uid),
87       is_memory_(false),
88       parser_memory_db_handle_(nullptr, sqlite3_close_v2) {
89   char* tmp_path;
90
91   if (global_parser_file_db_path_.empty()) {
92     tmp_path = getUserPkgParserDBPathUID(GetGlobalUID());
93     global_parser_file_db_path_ = tmp_path;
94     free(tmp_path);
95
96     tmp_path = getUserPkgCertDBPath();
97     cert_file_db_path_ = tmp_path;
98     free(tmp_path);
99   }
100
101   tmp_path = getUserPkgParserDBPathUID(uid_);
102   parser_file_db_path_ = tmp_path;
103   free(tmp_path);
104
105   parser_memory_db_path_ = "file:parserdb" +
106                            std::to_string(static_cast<int>(uid_)) +
107                            "?mode=memory&cache=shared";
108 }
109
110 DBHandleProvider& DBHandleProvider::GetInst(uid_t uid) {
111   static std::mutex singleton_lock_;
112   std::unique_lock<std::mutex> u(singleton_lock_);
113
114   uid = ConvertUID(uid);
115   auto& prov = provider_[uid];
116   if (prov == nullptr)
117     prov.reset(new DBHandleProvider(uid));
118
119   return *prov;
120 }
121
122 bool DBHandleProvider::IsCrashedWriteRequest() {
123   std::unique_lock<std::mutex> u(pid_list_lock_);
124
125   if (pid_list_.empty())
126     return false;
127
128   bool ret = true;
129   LOG(DEBUG) << "Check process count : " << pid_list_.size();
130   std::vector<pid_t> remove_pids;
131   for (pid_t pid : pid_list_) {
132     std::string status_path = "/proc/" + std::to_string(pid) + "/status";
133
134     int fd = open(status_path.c_str(), O_RDONLY);
135     if (fd < 0) {
136       LOG(ERROR) << "Process is crashed : " << pid;
137       remove_pids.push_back(pid);
138     } else {
139       ret = false;
140       close(fd);
141     }
142   }
143
144   for (pid_t pid : remove_pids)
145     pid_list_.erase(pid);
146
147   return ret;
148 }
149
150 std::vector<std::pair<std::string, uid_t>> DBHandleProvider::GetParserDBPath(
151     pid_t pid, bool write) {
152   std::unique_lock<std::recursive_mutex> u(lock_);
153   std::vector<std::pair<std::string, uid_t>> db_path_list;
154
155   if (is_memory_ != is_memory_global_)
156     is_memory_global_ ? SetMemoryMode(pid) : UnsetMemoryMode(pid);
157
158   if (IsMemoryDBActive(pid, write)) {
159     if (uid_ > REGULAR_USER)
160       db_path_list.emplace_back(std::make_pair(parser_memory_db_path_, uid_));
161     db_path_list.emplace_back(
162         std::make_pair(global_parser_memory_db_path, GetGlobalUID()));
163   } else {
164     if (uid_ > REGULAR_USER)
165       db_path_list.emplace_back(std::make_pair(parser_file_db_path_, uid_));
166     db_path_list.emplace_back(
167         std::make_pair(global_parser_file_db_path_, GetGlobalUID()));
168   }
169
170   if (db_path_list.size() == 1) {
171     LOG(DEBUG) << "global db path : " << db_path_list[0].first;
172   } else {
173     LOG(DEBUG) << "local db path : " << db_path_list[0].first;
174     LOG(DEBUG) << "global db path : " << db_path_list[1].first;
175   }
176
177   return db_path_list;
178 }
179
180 std::string DBHandleProvider::GetCertDBPath(pid_t pid, bool write) {
181   std::unique_lock<std::recursive_mutex> u(lock_);
182   if (is_memory_ != is_memory_global_)
183     is_memory_global_ ? SetMemoryMode(pid) : UnsetMemoryMode(pid);
184
185   if (IsMemoryDBActive(pid, write))
186     return cert_memory_db_path;
187   else
188     return cert_file_db_path_;
189 }
190
191 sqlite3* DBHandleProvider::GetMemoryDBHandle(const std::string& filedb_path,
192                                              const std::string& memorydb_path) {
193   sqlite3* memorydb = nullptr;
194   sqlite3* filedb = nullptr;
195   int ret = sqlite3_open_v2(memorydb_path.c_str(), &memorydb,
196                             SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, nullptr);
197   if (ret != SQLITE_OK) {
198     LOG(ERROR) << "Failed to open memory DB " << ret << ": " << memorydb_path;
199     return nullptr;
200   }
201
202   ret = sqlite3_open_v2(filedb_path.c_str(), &filedb, SQLITE_OPEN_READONLY,
203                         nullptr);
204   if (ret != SQLITE_OK) {
205     LOG(ERROR) << "Failed to open file DB " << ret << ": " << filedb_path;
206     sqlite3_close_v2(memorydb);
207     return nullptr;
208   }
209
210   sqlite3_backup* backup =
211       sqlite3_backup_init(memorydb, "main", filedb, "main");
212   if (backup == nullptr) {
213     LOG(ERROR) << "Failed to backup for memory DB";
214     sqlite3_close_v2(memorydb);
215     sqlite3_close_v2(filedb);
216     return nullptr;
217   }
218
219   sqlite3_backup_step(backup, -1);
220   sqlite3_backup_finish(backup);
221   sqlite3_close_v2(filedb);
222   return memorydb;
223 }
224
225 void DBHandleProvider::SetMemoryMode(pid_t pid) {
226   std::unique_lock<std::recursive_mutex> u(lock_);
227   if (is_memory_global_ && is_memory_)
228     return;
229
230   sqlite3* parser_db =
231       GetMemoryDBHandle(parser_file_db_path_, parser_memory_db_path_);
232   if (parser_db != nullptr)
233     parser_memory_db_handle_.reset(parser_db);
234
235   if (is_memory_ == is_memory_global_) {
236     sqlite3* global_parser_file_db = GetMemoryDBHandle(
237         global_parser_file_db_path_, global_parser_memory_db_path);
238     if (global_parser_file_db)
239       global_parser_memory_db_handle_.reset(global_parser_file_db);
240     sqlite3* cert_db =
241         GetMemoryDBHandle(cert_file_db_path_, cert_memory_db_path);
242     if (cert_db != nullptr)
243       cert_memory_db_handle_.reset(cert_db);
244
245     InsertPID(pid);
246   }
247
248   is_memory_ = true;
249   is_memory_global_ = true;
250   LOG(DEBUG) << "Set Memory mode : Memory";
251 }
252
253 void DBHandleProvider::UnsetMemoryMode(pid_t pid) {
254   std::unique_lock<std::recursive_mutex> u(lock_);
255   if (!is_memory_global_ && !is_memory_)
256     return;
257
258   auto lock = CacheFlag::GetWriterLock();
259   CacheFlag::SetStatus(CacheFlag::Status::PREPARING);
260
261   parser_memory_db_handle_.reset(nullptr);
262   cert_memory_db_handle_.reset(nullptr);
263   global_parser_memory_db_handle_.reset(nullptr);
264
265   if (!ErasePID(pid))
266     LOG(ERROR) << "Given pid is not exists in pid list : " << pid;
267
268   is_memory_ = false;
269   is_memory_global_ = false;
270   pkg_map_.clear();
271   app_map_.clear();
272   CacheFlag::SetStatus(CacheFlag::Status::UNPREPARED);
273   LOG(DEBUG) << "Set Memory mode : File";
274 }
275
276 bool DBHandleProvider::IsMemoryDBActive(pid_t pid, bool write) {
277   std::unique_lock<std::mutex> u(pid_list_lock_);
278   return (is_memory_ && pid_list_.find(pid) == pid_list_.end() && !write);
279 }
280
281 void DBHandleProvider::TrimCache() {
282   std::unique_lock<std::recursive_mutex> u(lock_);
283   if (!released_)
284     ReleaseCache();
285 }
286
287 void DBHandleProvider::ReleaseCache() {
288   auto lock = CacheFlag::GetWriterLock();
289   CacheFlag::SetStatus(CacheFlag::Status::PREPARING);
290
291   app_map_.clear();
292   pkg_map_.clear();
293   CacheFlag::SetStatus(CacheFlag::Status::UNPREPARED);
294
295   released_ = true;
296 }
297
298 bool DBHandleProvider::IsWriter(pid_t pid) {
299   std::unique_lock<std::mutex> u(pid_list_lock_);
300   return pid_list_.find(pid) != pid_list_.end();
301 }
302
303 int DBHandleProvider::UpdateCache(sqlite3* db, pid_t pid, uid_t uid, bool write,
304     const std::string& locale) {
305   pkg_map_.clear();
306   app_map_.clear();
307
308   GHashTable* list = g_hash_table_new(g_str_hash, g_str_equal);
309   if (list == nullptr) {
310     LOG(ERROR) << "Out of memory";
311     return PMINFO_R_ERROR;
312   }
313
314   auto tmp_filter = reinterpret_cast<pkgmgrinfo_filter_x*>(
315       calloc(1, sizeof(pkgmgrinfo_filter_x)));
316   if (tmp_filter == nullptr) {
317     LOG(ERROR) << "Out of memory";
318     g_hash_table_destroy(list);
319     return PMINFO_R_ERROR;
320   }
321
322   int ret = pkginfo_internal_filter_get_list(db, tmp_filter, uid_,
323                                              locale.c_str(), list);
324   if (ret == PMINFO_R_OK) {
325     GHashTableIter iter;
326     gpointer value;
327     g_hash_table_iter_init(&iter, list);
328     while (g_hash_table_iter_next(&iter, nullptr, &value)) {
329       auto* pkg = reinterpret_cast<package_x*>(value);
330       AddPackage(pkg->package, pkg);
331     }
332   }
333
334   g_hash_table_destroy(list);
335   if (ret == PMINFO_R_ERROR) {
336     free(tmp_filter);
337     return ret;
338   }
339
340   list = g_hash_table_new(g_str_hash, g_str_equal);
341   ret = appinfo_internal_filter_get_list(db, tmp_filter, uid_, uid,
342                                          locale.c_str(), list);
343   free(tmp_filter);
344   if (ret == PMINFO_R_OK) {
345     GHashTableIter iter;
346     gpointer value;
347     g_hash_table_iter_init(&iter, list);
348     while (g_hash_table_iter_next(&iter, nullptr, &value)) {
349       auto* app = reinterpret_cast<application_x*>(value);
350       app->privileges = pkg_map_[app->package].front()->privileges;
351       AddApplication(app->appid, app);
352     }
353   }
354   g_hash_table_destroy(list);
355   released_ = false;
356
357   return ret;
358 }
359
360 std::vector<std::shared_ptr<package_x>> DBHandleProvider::GetPackages(
361     pid_t pid, bool write, pkgmgrinfo_filter_x* filter,
362     const std::string& package) {
363   std::vector<std::shared_ptr<package_x>> ret;
364   for (auto& info : pkg_map_[package]) {
365     bool pass = true;
366     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
367       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
368       auto* checker = FilterCheckerProvider::GetInst().
369           GetPkgFilterChecker(node->prop);
370       if (!checker->CheckFilter(node, info.get())) {
371         pass = false;
372         break;
373       }
374     }
375     if (pass)
376       ret.push_back(info);
377   }
378
379   return ret;
380 }
381
382 void DBHandleProvider::AddPackage(std::string package, package_x* info) {
383   auto ptr = std::shared_ptr<package_x>(info, pkgmgrinfo_basic_free_package);
384   pkg_map_[package].push_back(ptr);
385   pkg_map_[""].push_back(std::move(ptr));
386 }
387
388 std::vector<std::shared_ptr<application_x>> DBHandleProvider::GetApplications(
389     pid_t pid, bool write, pkgmgrinfo_filter_x* filter,
390     const std::string& app) {
391   /* make metadata filter map */
392   std::unordered_map<std::string, std::string> metadata_map;
393   for (auto* it = filter->list_metadata; it != nullptr; it = g_slist_next(it)) {
394     auto node = reinterpret_cast<pkgmgrinfo_metadata_node_x*>(it->data);
395     if (node->key == nullptr)
396       continue;
397
398     metadata_map[node->key] = (node->value ? node->value : "");
399   }
400
401   std::vector<std::shared_ptr<application_x>> ret;
402   for (auto& info : app_map_[app]) {
403     bool pass = true;
404     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
405       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
406       auto* checker = FilterCheckerProvider::GetInst().
407           GetAppFilterChecker(node->prop);
408       if (!checker->CheckFilter(node, info.get())) {
409         pass = false;
410         break;
411       }
412     }
413     if (!pass)
414       continue;
415
416     if (!metadata_map.empty()) {
417       pass = false;
418       for (auto* it = info->metadata; it != nullptr; it = g_list_next(it)) {
419         auto* node = reinterpret_cast<metadata_x*>(it->data);
420         if (node->key != nullptr) {
421           auto metadata = metadata_map.find(node->key);
422           if (metadata != metadata_map.end() &&
423               strstr(node->value ? node->value : "",
424                   metadata->second.c_str()) != nullptr) {
425             pass = true;
426             break;
427           }
428         }
429       }
430     }
431
432     if (pass)
433       ret.push_back(info);
434   }
435   return ret;
436 }
437
438 void DBHandleProvider::AddApplication(std::string app, application_x* info) {
439   auto ptr =
440       std::shared_ptr<application_x>(info, pkgmgrinfo_basic_free_application);
441   app_map_[app].push_back(ptr);
442   app_map_[""].push_back(std::move(ptr));
443 }
444
445 void DBHandleProvider::InsertPID(pid_t pid) {
446   std::unique_lock<std::mutex> u(pid_list_lock_);
447
448   pid_list_.insert(pid);
449 }
450
451 bool DBHandleProvider::ErasePID(pid_t pid) {
452   std::unique_lock<std::mutex> u(pid_list_lock_);
453
454   return pid_list_.erase(pid) == 1;
455 }
456
457 }  // namespace database
458 }  // namespace pkgmgr_server