Remove unnecessary unique_lock from read operation
[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/stat.h>
22 #include <sys/types.h>
23
24 #include <tzplatform_config.h>
25
26 #include <algorithm>
27 #include <string>
28 #include <vector>
29
30 #include "cache_flag.hh"
31 #include "utils/logging.hh"
32
33 #include "pkgmgr-info.h"
34 #include "pkgmgrinfo_debug.h"
35 #include "pkgmgrinfo_internal.h"
36 #include "pkgmgrinfo_internal.hh"
37 #include "pkgmgrinfo_private.h"
38
39 #ifdef LOG_TAG
40 #undef LOG_TAG
41 #endif
42 #define LOG_TAG "PKGMGR_INFO"
43
44 namespace {
45
46 uid_t globaluser_uid = -1;
47
48 uid_t GetGlobalUID() {
49   if (globaluser_uid == (uid_t)-1)
50     globaluser_uid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
51
52   return globaluser_uid;
53 }
54
55 uid_t ConvertUID(uid_t uid) {
56   if (uid < REGULAR_USER)
57     return GetGlobalUID();
58   else
59     return uid;
60 }
61
62 bool GetModifiedTime(const char* dbpath, timespec* t) {
63   if (dbpath == nullptr || t == nullptr) {
64     LOG(ERROR) << "Invalid argument";
65     return false;
66   }
67
68   struct stat attr;
69   if (stat(dbpath, &attr)) {
70     LOG(ERROR) << "Fail to get status from file "
71         << dbpath << " errno : " << errno;
72     return false;
73   }
74
75   *t = attr.st_mtim;
76
77   return true;
78 }
79
80 static const std::string global_parser_memdb_path =
81     "file:parserdb?mode=memory&cache=shared";
82
83 static const std::string cert_memdb_path =
84     "file:certdb?mode=memory&cache=shared";
85
86 }  // namespace
87
88 namespace pkgmgr_server {
89 namespace database {
90
91 std::unordered_map<uid_t, std::unique_ptr<DBHandleProvider>>
92     DBHandleProvider::provider_map_;
93 bool DBHandleProvider::is_global_memdb_set_ = false;
94 std::unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
95     DBHandleProvider::global_parser_memdb_handle_(nullptr,
96                                                   sqlite3_close_v2);
97 std::unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
98     DBHandleProvider::cert_memdb_handle_(nullptr, sqlite3_close_v2);
99 std::string DBHandleProvider::global_parser_filedb_path_;
100 std::string DBHandleProvider::cert_filedb_path_;
101 std::unordered_set<pid_t> DBHandleProvider::writer_pid_list_;
102 std::recursive_mutex DBHandleProvider::lock_;
103 std::shared_mutex DBHandleProvider::pid_list_lock_;
104
105 DBHandleProvider::DBHandleProvider(uid_t uid)
106     : uid_(uid),
107       is_user_memdb_set_(false),
108       parser_memdb_handle_(nullptr, sqlite3_close_v2) {
109   char* tmp_path;
110
111   if (global_parser_filedb_path_.empty()) {
112     tmp_path = getUserPkgParserDBPathUID(GetGlobalUID());
113     global_parser_filedb_path_ = tmp_path;
114     free(tmp_path);
115   }
116
117   if (cert_filedb_path_.empty()) {
118     tmp_path = getUserPkgCertDBPath();
119     cert_filedb_path_ = tmp_path;
120     free(tmp_path);
121   }
122
123   tmp_path = getUserPkgParserDBPathUID(uid_);
124   user_parser_filedb_path_ = tmp_path;
125   free(tmp_path);
126
127   user_parser_memdb_path_ = "file:parserdb" +
128                             std::to_string(static_cast<int>(uid_)) +
129                             "?mode=memory&cache=shared";
130 }
131
132 DBHandleProvider& DBHandleProvider::GetInst(uid_t uid) {
133   static std::shared_mutex singleton_lock_;
134   std::shared_lock<std::shared_mutex> s(singleton_lock_);
135
136   uid = ConvertUID(uid);
137   auto it = provider_map_.find(uid);
138   if (it != provider_map_.end())
139     return *(it->second);
140
141   s.unlock();
142   std::unique_lock<std::shared_mutex> u(singleton_lock_);
143   auto& prov = provider_map_[uid];
144   if (prov == nullptr)
145     prov.reset(new DBHandleProvider(uid));
146
147   return *prov;
148 }
149
150 bool DBHandleProvider::IsCrashedWriteRequest() {
151   std::unique_lock<std::shared_mutex> u(pid_list_lock_);
152
153   if (writer_pid_list_.empty())
154     return false;
155
156   bool ret = true;
157   LOG(DEBUG) << "Check process count : " << writer_pid_list_.size();
158   std::vector<pid_t> remove_pids;
159   for (pid_t pid : writer_pid_list_) {
160     std::string status_path = "/proc/" + std::to_string(pid) + "/status";
161
162     int fd = open(status_path.c_str(), O_RDONLY);
163     if (fd < 0) {
164       LOG(ERROR) << "Process is crashed : " << pid;
165       remove_pids.push_back(pid);
166     } else {
167       ret = false;
168       close(fd);
169     }
170   }
171
172   for (pid_t pid : remove_pids)
173     writer_pid_list_.erase(pid);
174
175   return ret;
176 }
177
178 std::vector<std::pair<std::string, uid_t>> DBHandleProvider::GetParserDBPath(
179     pid_t pid, bool write) {
180   std::unique_lock<std::recursive_mutex> u(lock_);
181   std::vector<std::pair<std::string, uid_t>> db_path_list;
182
183   if (is_user_memdb_set_ != is_global_memdb_set_)
184     is_global_memdb_set_ ? SetMemoryMode(pid) : UnsetMemoryMode(pid);
185
186   if (IsMemoryDBActive(pid, write)) {
187     if (uid_ > REGULAR_USER)
188       db_path_list.emplace_back(std::make_pair(user_parser_memdb_path_, uid_));
189
190     db_path_list.emplace_back(
191         std::make_pair(global_parser_memdb_path, GetGlobalUID()));
192   } else {
193     if (uid_ > REGULAR_USER)
194       db_path_list.emplace_back(std::make_pair(user_parser_filedb_path_, uid_));
195
196     db_path_list.emplace_back(
197         std::make_pair(global_parser_filedb_path_, GetGlobalUID()));
198   }
199
200   if (db_path_list.size() == 1) {
201     LOG(DEBUG) << "global db path : " << db_path_list[0].first;
202   } else {
203     LOG(DEBUG) << "local db path : " << db_path_list[0].first;
204     LOG(DEBUG) << "global db path : " << db_path_list[1].first;
205   }
206
207   return db_path_list;
208 }
209
210 std::string DBHandleProvider::GetCertDBPath(pid_t pid, bool write) {
211   std::unique_lock<std::recursive_mutex> u(lock_);
212   if (is_user_memdb_set_ != is_global_memdb_set_)
213     is_global_memdb_set_ ? SetMemoryMode(pid) : UnsetMemoryMode(pid);
214
215   if (IsMemoryDBActive(pid, write))
216     return cert_memdb_path;
217   else
218     return cert_filedb_path_;
219 }
220
221 sqlite3* DBHandleProvider::CreateMemoryDBHandle(const std::string& filedb_path,
222                                              const std::string& memorydb_path) {
223   sqlite3* memorydb = nullptr;
224   sqlite3* filedb = nullptr;
225   int ret = sqlite3_open_v2(memorydb_path.c_str(), &memorydb,
226                             SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, nullptr);
227   if (ret != SQLITE_OK) {
228     LOG(ERROR) << "Failed to open memory DB " << ret << ": " << memorydb_path;
229     return nullptr;
230   }
231
232   ret = sqlite3_open_v2(filedb_path.c_str(), &filedb, SQLITE_OPEN_READONLY,
233                         nullptr);
234   if (ret != SQLITE_OK) {
235     LOG(ERROR) << "Failed to open file DB " << ret << ": " << filedb_path;
236     sqlite3_close_v2(memorydb);
237     return nullptr;
238   }
239
240   sqlite3_backup* backup =
241       sqlite3_backup_init(memorydb, "main", filedb, "main");
242   if (backup == nullptr) {
243     LOG(ERROR) << "Failed to backup for memory DB";
244     sqlite3_close_v2(memorydb);
245     sqlite3_close_v2(filedb);
246     return nullptr;
247   }
248
249   sqlite3_backup_step(backup, -1);
250   sqlite3_backup_finish(backup);
251   sqlite3_close_v2(filedb);
252   return memorydb;
253 }
254
255 void DBHandleProvider::SetMemoryMode(pid_t pid) {
256   std::unique_lock<std::recursive_mutex> u(lock_);
257   if (is_global_memdb_set_ && is_user_memdb_set_)
258     return;
259
260   sqlite3* parser_db =
261       CreateMemoryDBHandle(user_parser_filedb_path_, user_parser_memdb_path_);
262   if (parser_db != nullptr)
263     parser_memdb_handle_.reset(parser_db);
264
265   if (is_user_memdb_set_ == is_global_memdb_set_) {
266     sqlite3* global_parser_file_db = CreateMemoryDBHandle(
267         global_parser_filedb_path_, global_parser_memdb_path);
268     if (global_parser_file_db)
269       global_parser_memdb_handle_.reset(global_parser_file_db);
270
271     sqlite3* cert_db =
272         CreateMemoryDBHandle(cert_filedb_path_, cert_memdb_path);
273     if (cert_db != nullptr)
274       cert_memdb_handle_.reset(cert_db);
275
276     InsertPID(pid);
277   }
278
279   is_user_memdb_set_ = true;
280   is_global_memdb_set_ = true;
281   LOG(DEBUG) << "Set Memory mode : Memory";
282 }
283
284 void DBHandleProvider::UnsetMemoryMode(pid_t pid) {
285   std::unique_lock<std::recursive_mutex> u(lock_);
286   if (!is_global_memdb_set_ && !is_user_memdb_set_)
287     return;
288
289   parser_memdb_handle_.reset(nullptr);
290   cert_memdb_handle_.reset(nullptr);
291   global_parser_memdb_handle_.reset(nullptr);
292
293   if (!ErasePID(pid))
294     LOG(ERROR) << "Given pid is not exists in pid list : " << pid;
295
296   is_user_memdb_set_ = false;
297   is_global_memdb_set_ = false;
298
299   TrimCache();
300
301   LOG(DEBUG) << "Set Memory mode : File";
302 }
303
304 bool DBHandleProvider::IsMemoryDBActive(pid_t pid, bool write) {
305   std::unique_lock<std::shared_mutex> u(pid_list_lock_);
306   if (!is_user_memdb_set_)
307     return false;
308
309   if (write)
310     return false;
311
312   if (writer_pid_list_.find(pid) != writer_pid_list_.end())
313     return false;
314
315   return true;
316 }
317
318 void DBHandleProvider::TrimCache() {
319   std::unique_lock<std::recursive_mutex> u(lock_);
320   if (!released_)
321     ReleaseCache();
322 }
323
324 void DBHandleProvider::ReleaseCache() {
325   auto lock = CacheFlag::GetWriterLock();
326   CacheFlag::SetStatus(CacheFlag::Status::PREPARING);
327
328   app_map_.clear();
329   pkg_map_.clear();
330   CacheFlag::SetStatus(CacheFlag::Status::UNPREPARED);
331
332   released_ = true;
333 }
334
335 bool DBHandleProvider::IsWriter(pid_t pid) {
336   std::unique_lock<std::shared_mutex> s(pid_list_lock_);
337   return writer_pid_list_.find(pid) != writer_pid_list_.end();
338 }
339
340 int DBHandleProvider::UpdateCache(sqlite3* db, pid_t pid, uid_t uid, bool write,
341     const std::string& locale) {
342   pkg_map_.clear();
343   app_map_.clear();
344
345   const char* dbpath = sqlite3_db_filename(db, "main");
346   bool is_inmemory_db = false;
347   if (dbpath == nullptr || strlen(dbpath) == 0) {
348     LOG(INFO) << "database is inmemory db";
349     is_inmemory_db = true;
350   }
351
352   timespec start_time = { 0, };
353   timespec end_time = { 0, };
354   if (!is_inmemory_db && !GetModifiedTime(dbpath, &start_time))
355     return PMINFO_R_ERROR;
356
357   GHashTable* list = g_hash_table_new(g_str_hash, g_str_equal);
358   if (list == nullptr) {
359     LOG(ERROR) << "Out of memory";
360     return PMINFO_R_ERROR;
361   }
362
363   pkgmgrinfo_filter_x tmp_filter = { 0, };
364   tmp_filter.cache_flag = true;
365   int ret = pkginfo_internal_filter_get_list(db, &tmp_filter, uid_,
366                                              locale.c_str(), list);
367   if (ret == PMINFO_R_OK) {
368     GHashTableIter iter;
369     gpointer value;
370     g_hash_table_iter_init(&iter, list);
371     while (g_hash_table_iter_next(&iter, nullptr, &value)) {
372       auto* pkg = reinterpret_cast<package_x*>(value);
373       std::string pkgid = pkg->package;
374       AddPackage(std::move(pkgid), pkg);
375     }
376   }
377
378   g_hash_table_destroy(list);
379   if (ret == PMINFO_R_ERROR)
380     return ret;
381
382   if (!is_inmemory_db && !GetModifiedTime(dbpath, &end_time))
383     return PMINFO_R_ERROR;
384
385   if (start_time.tv_sec != end_time.tv_sec ||
386       start_time.tv_nsec != end_time.tv_nsec) {
387     LOG(ERROR) << "Database(" << dbpath << ") modification was detected";
388     return PMINFO_R_ERROR;
389   }
390
391   std::vector<std::shared_ptr<application_x>> app_list;
392   ret = pkgmgr_server::internal::appinfo_internal_filter_get_list(db,
393       &tmp_filter, uid_, uid, locale.c_str(), app_list);
394
395   if (!is_inmemory_db && !GetModifiedTime(dbpath, &end_time))
396     return PMINFO_R_ERROR;
397
398   if (start_time.tv_sec != end_time.tv_sec ||
399       start_time.tv_nsec != end_time.tv_nsec) {
400     LOG(ERROR) << "Database(" << dbpath << ") modification was detected";
401     return PMINFO_R_ERROR;
402   }
403
404   if (ret == PMINFO_R_OK) {
405     for (auto& app : app_list) {
406       if (pkg_map_.find(app->package) == pkg_map_.end()) {
407         LOG(ERROR) << "Can not find package from pkg_map";
408         return PMINFO_R_ERROR;
409       }
410
411       app->privileges = pkg_map_[app->package].front()->privileges;
412       std::string appid = app->appid;
413       AddApplication(std::move(appid), std::move(app));
414     }
415   }
416
417   released_ = false;
418
419   return ret;
420 }
421
422 std::vector<std::shared_ptr<package_x>> DBHandleProvider::GetPackages(
423     pid_t pid, pkgmgrinfo_filter_x* filter,
424     const std::string& package) {
425   std::vector<std::shared_ptr<package_x>> ret;
426   auto map_it = pkg_map_.find(package);
427   if (map_it == pkg_map_.end())
428     return ret;
429
430   if (__check_package_storage_status(filter)) {
431     if (pkgmgrinfo_pkginfo_filter_add_bool(filter,
432         PMINFO_PKGINFO_PROP_PACKAGE_CHECK_STORAGE, true) != PMINFO_R_OK) {
433       LOG(ERROR) << "Fail to add check storage value to filter";
434       return {};
435     }
436   }
437
438   for (auto& info : map_it->second) {
439     bool pass = true;
440     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
441       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
442       auto* checker = FilterCheckerProvider::GetInst().
443           GetPkgFilterChecker(node->prop);
444       if (!checker->CheckFilter(node, info.get())) {
445         pass = false;
446         break;
447       }
448     }
449     if (pass)
450       ret.push_back(info);
451   }
452
453   return ret;
454 }
455
456 void DBHandleProvider::AddPackage(std::string package, package_x* info) {
457   auto ptr = std::shared_ptr<package_x>(info, pkgmgrinfo_basic_free_package);
458   pkg_map_[package].push_back(ptr);
459   pkg_map_[""].push_back(std::move(ptr));
460 }
461
462 std::vector<std::shared_ptr<application_x>> DBHandleProvider::GetApplications(
463     pid_t pid, pkgmgrinfo_filter_x* filter,
464     const std::string& app) {
465   /* make metadata filter map */
466   std::unordered_map<std::string, std::string> metadata_map;
467   for (auto* it = filter->list_metadata; it != nullptr; it = g_slist_next(it)) {
468     auto node = reinterpret_cast<pkgmgrinfo_metadata_node_x*>(it->data);
469     if (node->key == nullptr)
470       continue;
471
472     metadata_map[node->key] = (node->value ? node->value : "");
473   }
474
475   std::vector<std::shared_ptr<application_x>> ret;
476   auto map_it = app_map_.find(app);
477   if (map_it == app_map_.end())
478     return ret;
479
480   if (pkgmgr_server::internal::check_app_storage_status(filter)) {
481     if (pkgmgrinfo_appinfo_filter_add_bool(filter,
482         PMINFO_APPINFO_PROP_APP_CHECK_STORAGE, true) != PMINFO_R_OK) {
483       LOG(ERROR) << "Fail to add check storage value to filter";
484       return {};
485     }
486   }
487
488   for (auto& info : map_it->second) {
489     bool pass = true;
490     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
491       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
492       auto* checker = FilterCheckerProvider::GetInst().
493           GetAppFilterChecker(node->prop);
494       if (!checker->CheckFilter(node, info.get())) {
495         pass = false;
496         break;
497       }
498     }
499     if (!pass)
500       continue;
501
502     if (!metadata_map.empty()) {
503       pass = false;
504       for (auto* it = info->metadata; it != nullptr; it = g_list_next(it)) {
505         auto* node = reinterpret_cast<metadata_x*>(it->data);
506         if (node->key != nullptr) {
507           auto metadata = metadata_map.find(node->key);
508           if (metadata == metadata_map.end())
509             continue;
510
511           if (metadata->second.empty() ||
512               strcmp(node->value ? node->value : "",
513                   metadata->second.c_str()) == 0) {
514             pass = true;
515             break;
516           }
517         }
518       }
519     }
520
521     if (pass)
522       ret.push_back(info);
523   }
524   return ret;
525 }
526
527 void DBHandleProvider::AddApplication(std::string app, std::shared_ptr<application_x> info) {
528   app_map_[app].push_back(info);
529   app_map_[""].push_back(std::move(info));
530 }
531
532 void DBHandleProvider::InsertPID(pid_t pid) {
533   std::unique_lock<std::shared_mutex> u(pid_list_lock_);
534
535   writer_pid_list_.insert(pid);
536 }
537
538 bool DBHandleProvider::ErasePID(pid_t pid) {
539   std::unique_lock<std::shared_mutex> u(pid_list_lock_);
540
541   return writer_pid_list_.erase(pid) == 1;
542 }
543
544 }  // namespace database
545 }  // namespace pkgmgr_server