Check if database is modified when making cache
[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::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::mutex singleton_lock_;
134   std::unique_lock<std::mutex> u(singleton_lock_);
135
136   uid = ConvertUID(uid);
137   auto& prov = provider_map_[uid];
138   if (prov == nullptr)
139     prov.reset(new DBHandleProvider(uid));
140
141   return *prov;
142 }
143
144 bool DBHandleProvider::IsCrashedWriteRequest() {
145   std::unique_lock<std::mutex> u(pid_list_lock_);
146
147   if (writer_pid_list_.empty())
148     return false;
149
150   bool ret = true;
151   LOG(DEBUG) << "Check process count : " << writer_pid_list_.size();
152   std::vector<pid_t> remove_pids;
153   for (pid_t pid : writer_pid_list_) {
154     std::string status_path = "/proc/" + std::to_string(pid) + "/status";
155
156     int fd = open(status_path.c_str(), O_RDONLY);
157     if (fd < 0) {
158       LOG(ERROR) << "Process is crashed : " << pid;
159       remove_pids.push_back(pid);
160     } else {
161       ret = false;
162       close(fd);
163     }
164   }
165
166   for (pid_t pid : remove_pids)
167     writer_pid_list_.erase(pid);
168
169   return ret;
170 }
171
172 std::vector<std::pair<std::string, uid_t>> DBHandleProvider::GetParserDBPath(
173     pid_t pid, bool write) {
174   std::unique_lock<std::recursive_mutex> u(lock_);
175   std::vector<std::pair<std::string, uid_t>> db_path_list;
176
177   if (is_user_memdb_set_ != is_global_memdb_set_)
178     is_global_memdb_set_ ? SetMemoryMode(pid) : UnsetMemoryMode(pid);
179
180   if (IsMemoryDBActive(pid, write)) {
181     if (uid_ > REGULAR_USER)
182       db_path_list.emplace_back(std::make_pair(user_parser_memdb_path_, uid_));
183
184     db_path_list.emplace_back(
185         std::make_pair(global_parser_memdb_path, GetGlobalUID()));
186   } else {
187     if (uid_ > REGULAR_USER)
188       db_path_list.emplace_back(std::make_pair(user_parser_filedb_path_, uid_));
189
190     db_path_list.emplace_back(
191         std::make_pair(global_parser_filedb_path_, GetGlobalUID()));
192   }
193
194   if (db_path_list.size() == 1) {
195     LOG(DEBUG) << "global db path : " << db_path_list[0].first;
196   } else {
197     LOG(DEBUG) << "local db path : " << db_path_list[0].first;
198     LOG(DEBUG) << "global db path : " << db_path_list[1].first;
199   }
200
201   return db_path_list;
202 }
203
204 std::string DBHandleProvider::GetCertDBPath(pid_t pid, bool write) {
205   std::unique_lock<std::recursive_mutex> u(lock_);
206   if (is_user_memdb_set_ != is_global_memdb_set_)
207     is_global_memdb_set_ ? SetMemoryMode(pid) : UnsetMemoryMode(pid);
208
209   if (IsMemoryDBActive(pid, write))
210     return cert_memdb_path;
211   else
212     return cert_filedb_path_;
213 }
214
215 sqlite3* DBHandleProvider::CreateMemoryDBHandle(const std::string& filedb_path,
216                                              const std::string& memorydb_path) {
217   sqlite3* memorydb = nullptr;
218   sqlite3* filedb = nullptr;
219   int ret = sqlite3_open_v2(memorydb_path.c_str(), &memorydb,
220                             SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, nullptr);
221   if (ret != SQLITE_OK) {
222     LOG(ERROR) << "Failed to open memory DB " << ret << ": " << memorydb_path;
223     return nullptr;
224   }
225
226   ret = sqlite3_open_v2(filedb_path.c_str(), &filedb, SQLITE_OPEN_READONLY,
227                         nullptr);
228   if (ret != SQLITE_OK) {
229     LOG(ERROR) << "Failed to open file DB " << ret << ": " << filedb_path;
230     sqlite3_close_v2(memorydb);
231     return nullptr;
232   }
233
234   sqlite3_backup* backup =
235       sqlite3_backup_init(memorydb, "main", filedb, "main");
236   if (backup == nullptr) {
237     LOG(ERROR) << "Failed to backup for memory DB";
238     sqlite3_close_v2(memorydb);
239     sqlite3_close_v2(filedb);
240     return nullptr;
241   }
242
243   sqlite3_backup_step(backup, -1);
244   sqlite3_backup_finish(backup);
245   sqlite3_close_v2(filedb);
246   return memorydb;
247 }
248
249 void DBHandleProvider::SetMemoryMode(pid_t pid) {
250   std::unique_lock<std::recursive_mutex> u(lock_);
251   if (is_global_memdb_set_ && is_user_memdb_set_)
252     return;
253
254   sqlite3* parser_db =
255       CreateMemoryDBHandle(user_parser_filedb_path_, user_parser_memdb_path_);
256   if (parser_db != nullptr)
257     parser_memdb_handle_.reset(parser_db);
258
259   if (is_user_memdb_set_ == is_global_memdb_set_) {
260     sqlite3* global_parser_file_db = CreateMemoryDBHandle(
261         global_parser_filedb_path_, global_parser_memdb_path);
262     if (global_parser_file_db)
263       global_parser_memdb_handle_.reset(global_parser_file_db);
264
265     sqlite3* cert_db =
266         CreateMemoryDBHandle(cert_filedb_path_, cert_memdb_path);
267     if (cert_db != nullptr)
268       cert_memdb_handle_.reset(cert_db);
269
270     InsertPID(pid);
271   }
272
273   is_user_memdb_set_ = true;
274   is_global_memdb_set_ = true;
275   LOG(DEBUG) << "Set Memory mode : Memory";
276 }
277
278 void DBHandleProvider::UnsetMemoryMode(pid_t pid) {
279   std::unique_lock<std::recursive_mutex> u(lock_);
280   if (!is_global_memdb_set_ && !is_user_memdb_set_)
281     return;
282
283   parser_memdb_handle_.reset(nullptr);
284   cert_memdb_handle_.reset(nullptr);
285   global_parser_memdb_handle_.reset(nullptr);
286
287   if (!ErasePID(pid))
288     LOG(ERROR) << "Given pid is not exists in pid list : " << pid;
289
290   is_user_memdb_set_ = false;
291   is_global_memdb_set_ = false;
292
293   TrimCache();
294
295   LOG(DEBUG) << "Set Memory mode : File";
296 }
297
298 bool DBHandleProvider::IsMemoryDBActive(pid_t pid, bool write) {
299   std::unique_lock<std::mutex> u(pid_list_lock_);
300   if (!is_user_memdb_set_)
301     return false;
302
303   if (write)
304     return false;
305
306   if (writer_pid_list_.find(pid) != writer_pid_list_.end())
307     return false;
308
309   return true;
310 }
311
312 void DBHandleProvider::TrimCache() {
313   std::unique_lock<std::recursive_mutex> u(lock_);
314   if (!released_)
315     ReleaseCache();
316 }
317
318 void DBHandleProvider::ReleaseCache() {
319   auto lock = CacheFlag::GetWriterLock();
320   CacheFlag::SetStatus(CacheFlag::Status::PREPARING);
321
322   app_map_.clear();
323   pkg_map_.clear();
324   CacheFlag::SetStatus(CacheFlag::Status::UNPREPARED);
325
326   released_ = true;
327 }
328
329 bool DBHandleProvider::IsWriter(pid_t pid) {
330   std::unique_lock<std::mutex> u(pid_list_lock_);
331   return writer_pid_list_.find(pid) != writer_pid_list_.end();
332 }
333
334 int DBHandleProvider::UpdateCache(sqlite3* db, pid_t pid, uid_t uid, bool write,
335     const std::string& locale) {
336   pkg_map_.clear();
337   app_map_.clear();
338
339   GHashTable* list = g_hash_table_new(g_str_hash, g_str_equal);
340   if (list == nullptr) {
341     LOG(ERROR) << "Out of memory";
342     return PMINFO_R_ERROR;
343   }
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   pkgmgrinfo_filter_x tmp_filter = { 0, };
358   int ret = pkginfo_internal_filter_get_list(db, &tmp_filter, uid_,
359                                              locale.c_str(), list);
360   if (ret == PMINFO_R_OK) {
361     GHashTableIter iter;
362     gpointer value;
363     g_hash_table_iter_init(&iter, list);
364     while (g_hash_table_iter_next(&iter, nullptr, &value)) {
365       auto* pkg = reinterpret_cast<package_x*>(value);
366       std::string pkgid = pkg->package;
367       AddPackage(std::move(pkgid), pkg);
368     }
369   }
370
371   g_hash_table_destroy(list);
372   if (ret == PMINFO_R_ERROR)
373     return ret;
374
375   if (!is_inmemory_db && !GetModifiedTime(dbpath, &end_time))
376     return PMINFO_R_ERROR;
377
378   if (start_time.tv_sec != end_time.tv_sec ||
379       start_time.tv_nsec != end_time.tv_nsec) {
380     LOG(ERROR) << "Database(" << dbpath << ") modification was detected";
381     return PMINFO_R_ERROR;
382   }
383
384   std::vector<std::shared_ptr<application_x>> app_list;
385   ret = pkgmgr_server::internal::appinfo_internal_filter_get_list(db,
386       &tmp_filter, uid_, uid, locale.c_str(), app_list);
387
388   if (!is_inmemory_db && !GetModifiedTime(dbpath, &end_time))
389     return PMINFO_R_ERROR;
390
391   if (start_time.tv_sec != end_time.tv_sec ||
392       start_time.tv_nsec != end_time.tv_nsec) {
393     LOG(ERROR) << "Database(" << dbpath << ") modification was detected";
394     return PMINFO_R_ERROR;
395   }
396
397   if (ret == PMINFO_R_OK) {
398     for (auto& app : app_list) {
399       if (pkg_map_.find(app->package) == pkg_map_.end()) {
400         LOG(ERROR) << "Can not find package from pkg_map";
401         return PMINFO_R_ERROR;
402       }
403
404       app->privileges = pkg_map_[app->package].front()->privileges;
405       std::string appid = app->appid;
406       AddApplication(std::move(appid), std::move(app));
407     }
408   }
409
410   released_ = false;
411
412   return ret;
413 }
414
415 std::vector<std::shared_ptr<package_x>> DBHandleProvider::GetPackages(
416     pid_t pid, bool write, pkgmgrinfo_filter_x* filter,
417     const std::string& package) {
418   std::vector<std::shared_ptr<package_x>> ret;
419   for (auto& info : pkg_map_[package]) {
420     bool pass = true;
421     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
422       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
423       auto* checker = FilterCheckerProvider::GetInst().
424           GetPkgFilterChecker(node->prop);
425       if (!checker->CheckFilter(node, info.get())) {
426         pass = false;
427         break;
428       }
429     }
430     if (pass)
431       ret.push_back(info);
432   }
433
434   return ret;
435 }
436
437 void DBHandleProvider::AddPackage(std::string package, package_x* info) {
438   auto ptr = std::shared_ptr<package_x>(info, pkgmgrinfo_basic_free_package);
439   pkg_map_[package].push_back(ptr);
440   pkg_map_[""].push_back(std::move(ptr));
441 }
442
443 std::vector<std::shared_ptr<application_x>> DBHandleProvider::GetApplications(
444     pid_t pid, bool write, pkgmgrinfo_filter_x* filter,
445     const std::string& app) {
446   /* make metadata filter map */
447   std::unordered_map<std::string, std::string> metadata_map;
448   for (auto* it = filter->list_metadata; it != nullptr; it = g_slist_next(it)) {
449     auto node = reinterpret_cast<pkgmgrinfo_metadata_node_x*>(it->data);
450     if (node->key == nullptr)
451       continue;
452
453     metadata_map[node->key] = (node->value ? node->value : "");
454   }
455
456   std::vector<std::shared_ptr<application_x>> ret;
457   for (auto& info : app_map_[app]) {
458     bool pass = true;
459     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
460       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
461       auto* checker = FilterCheckerProvider::GetInst().
462           GetAppFilterChecker(node->prop);
463       if (!checker->CheckFilter(node, info.get())) {
464         pass = false;
465         break;
466       }
467     }
468     if (!pass)
469       continue;
470
471     if (!metadata_map.empty()) {
472       pass = false;
473       for (auto* it = info->metadata; it != nullptr; it = g_list_next(it)) {
474         auto* node = reinterpret_cast<metadata_x*>(it->data);
475         if (node->key != nullptr) {
476           auto metadata = metadata_map.find(node->key);
477           if (metadata == metadata_map.end())
478             continue;
479
480           if (metadata->second.empty() ||
481               strcmp(node->value ? node->value : "",
482                   metadata->second.c_str()) == 0) {
483             pass = true;
484             break;
485           }
486         }
487       }
488     }
489
490     if (pass)
491       ret.push_back(info);
492   }
493   return ret;
494 }
495
496 void DBHandleProvider::AddApplication(std::string app, std::shared_ptr<application_x> info) {
497   app_map_[app].push_back(info);
498   app_map_[""].push_back(std::move(info));
499 }
500
501 void DBHandleProvider::InsertPID(pid_t pid) {
502   std::unique_lock<std::mutex> u(pid_list_lock_);
503
504   writer_pid_list_.insert(pid);
505 }
506
507 bool DBHandleProvider::ErasePID(pid_t pid) {
508   std::unique_lock<std::mutex> u(pid_list_lock_);
509
510   return writer_pid_list_.erase(pid) == 1;
511 }
512
513 }  // namespace database
514 }  // namespace pkgmgr_server