14c3f3094fc95201fd329e95e37bbd8397c8bc42
[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   const char* dbpath = sqlite3_db_filename(db, "main");
340   bool is_inmemory_db = false;
341   if (dbpath == nullptr || strlen(dbpath) == 0) {
342     LOG(INFO) << "database is inmemory db";
343     is_inmemory_db = true;
344   }
345
346   timespec start_time = { 0, };
347   timespec end_time = { 0, };
348   if (!is_inmemory_db && !GetModifiedTime(dbpath, &start_time))
349     return PMINFO_R_ERROR;
350
351   GHashTable* list = g_hash_table_new(g_str_hash, g_str_equal);
352   if (list == nullptr) {
353     LOG(ERROR) << "Out of memory";
354     return PMINFO_R_ERROR;
355   }
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   auto map_it = pkg_map_.find(package);
420   if (map_it == pkg_map_.end())
421     return ret;
422
423   for (auto& info : map_it->second) {
424     bool pass = true;
425     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
426       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
427       auto* checker = FilterCheckerProvider::GetInst().
428           GetPkgFilterChecker(node->prop);
429       if (!checker->CheckFilter(node, info.get())) {
430         pass = false;
431         break;
432       }
433     }
434     if (pass)
435       ret.push_back(info);
436   }
437
438   return ret;
439 }
440
441 void DBHandleProvider::AddPackage(std::string package, package_x* info) {
442   auto ptr = std::shared_ptr<package_x>(info, pkgmgrinfo_basic_free_package);
443   pkg_map_[package].push_back(ptr);
444   pkg_map_[""].push_back(std::move(ptr));
445 }
446
447 std::vector<std::shared_ptr<application_x>> DBHandleProvider::GetApplications(
448     pid_t pid, bool write, pkgmgrinfo_filter_x* filter,
449     const std::string& app) {
450   /* make metadata filter map */
451   std::unordered_map<std::string, std::string> metadata_map;
452   for (auto* it = filter->list_metadata; it != nullptr; it = g_slist_next(it)) {
453     auto node = reinterpret_cast<pkgmgrinfo_metadata_node_x*>(it->data);
454     if (node->key == nullptr)
455       continue;
456
457     metadata_map[node->key] = (node->value ? node->value : "");
458   }
459
460   std::vector<std::shared_ptr<application_x>> ret;
461   auto map_it = app_map_.find(app);
462   if (map_it == app_map_.end())
463     return ret;
464
465   for (auto& info : map_it->second) {
466     bool pass = true;
467     for (auto* it = filter->list; it != nullptr; it = g_slist_next(it)) {
468       auto node = reinterpret_cast<pkgmgrinfo_node_x*>(it->data);
469       auto* checker = FilterCheckerProvider::GetInst().
470           GetAppFilterChecker(node->prop);
471       if (!checker->CheckFilter(node, info.get())) {
472         pass = false;
473         break;
474       }
475     }
476     if (!pass)
477       continue;
478
479     if (!metadata_map.empty()) {
480       pass = false;
481       for (auto* it = info->metadata; it != nullptr; it = g_list_next(it)) {
482         auto* node = reinterpret_cast<metadata_x*>(it->data);
483         if (node->key != nullptr) {
484           auto metadata = metadata_map.find(node->key);
485           if (metadata == metadata_map.end())
486             continue;
487
488           if (metadata->second.empty() ||
489               strcmp(node->value ? node->value : "",
490                   metadata->second.c_str()) == 0) {
491             pass = true;
492             break;
493           }
494         }
495       }
496     }
497
498     if (pass)
499       ret.push_back(info);
500   }
501   return ret;
502 }
503
504 void DBHandleProvider::AddApplication(std::string app, std::shared_ptr<application_x> info) {
505   app_map_[app].push_back(info);
506   app_map_[""].push_back(std::move(info));
507 }
508
509 void DBHandleProvider::InsertPID(pid_t pid) {
510   std::unique_lock<std::mutex> u(pid_list_lock_);
511
512   writer_pid_list_.insert(pid);
513 }
514
515 bool DBHandleProvider::ErasePID(pid_t pid) {
516   std::unique_lock<std::mutex> u(pid_list_lock_);
517
518   return writer_pid_list_.erase(pid) == 1;
519 }
520
521 }  // namespace database
522 }  // namespace pkgmgr_server