From: Ilho Kim Date: Thu, 7 Dec 2023 12:40:49 +0000 (+0900) Subject: Fix inconsistency between cache and database X-Git-Tag: accepted/tizen/8.0/unified/20231212.161340~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fappfw%2Fpkgmgr-info.git;a=commitdiff_plain;h=54073ac20bd49b3d664527b7f13b04290152e268 Fix inconsistency between cache and database If there is an installation process that was started before the cache creation task was completed on the server then that process can attempt to write directly to the database even after the cache generation is complete and a ready flag has been created Using inotify, watch for changes to the database and if there is an attempt to directly write to the database after caching has been completed, remove all cached data Change-Id: Ibf12fe472904467a8d5a5335f8525109aa03f32f Signed-off-by: Ilho Kim --- diff --git a/src/common/db_change_observer.cc b/src/common/db_change_observer.cc new file mode 100644 index 0000000..72ddb9d --- /dev/null +++ b/src/common/db_change_observer.cc @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "db_change_observer.hh" + +#include + +#include + +#include "utils/logging.hh" + +#include "pkgmgrinfo_debug.h" +#include "pkgmgr-info.h" + +#undef LOG_TAG +#define LOG_TAG "PKGMGR_INFO" + +namespace { + +uid_t globaluser_uid = -1; + +uid_t GetGlobalUID() { + if (globaluser_uid == (uid_t)-1) + globaluser_uid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER); + + return globaluser_uid; +} + +} // namespace + +namespace pkgmgr_common { + +DbChangeObserver& DbChangeObserver::GetInst() { + static DbChangeObserver inst; + + return inst; +} + +gboolean DbChangeObserver::OnReceiveEvent(GIOChannel* channel, GIOCondition cond, + gpointer user_data) { + LOG(WARNING) << "db changed event occured"; + char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); + auto db_change = reinterpret_cast(user_data); + int fd = g_io_channel_unix_get_fd(channel); + + while(read(fd, buf, sizeof(buf)) > 0); + + std::unique_lock u(db_change->lock_); + if (!db_change->changed_) { + db_change->changed_ = true; + if (db_change->listener_) + db_change->listener_->OnDbChanged(); + } + + return G_SOURCE_CONTINUE; +} + +DbChangeObserver::DbChangeObserver() { + std::unique_lock u(lock_); + SetGlobalParserDbPath(); +} + +DbChangeObserver::~DbChangeObserver() { + std::unique_lock u(lock_); + Dispose(); +} + +bool DbChangeObserver::Listen() { + std::unique_lock u(lock_); + + if (!disposed_) + return true; + + if (global_parser_db_path_.empty()) { + if (!SetGlobalParserDbPath()) { + LOG(ERROR) << "Fail to Set GlobalParserDbPath"; + return false; + } + } + + disposed_ = false; + + fd_ = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd_ == -1) { + LOG(ERROR) << "Failed to inotify_init. errno: " << errno; + Dispose(); + return false; + } + + wd_ = inotify_add_watch(fd_, global_parser_db_path_.c_str(), IN_MODIFY); + if (wd_ == -1) { + LOG(ERROR) << "Failed to inotify_add_watch. errno: " << errno; + Dispose(); + return false; + } + + channel_ = g_io_channel_unix_new(fd_); + if (channel_ == nullptr) { + LOG(ERROR) << "Failed to create GIO channel"; + Dispose(); + return false; + } + + tag_ = g_io_add_watch(channel_, (GIOCondition)(G_IO_IN), OnReceiveEvent, this); + if (tag_ == 0) { + LOG(ERROR) << "Failed to add watch"; + Dispose(); + return false; + } + + changed_ = false; + disposed_ = false; + + return true; +} + +void DbChangeObserver::StopListening() { + std::unique_lock u(lock_); + + Dispose(); +} + +bool DbChangeObserver::IsChanged() { + std::shared_lock s(lock_); + return changed_; +} + +void DbChangeObserver::SetChanged(bool changed) { + std::unique_locks (lock_); + changed_ = changed; +} + +void DbChangeObserver::Dispose() { + if (disposed_) + return; + + if (tag_) { + g_source_remove(tag_); + tag_ = 0; + } + + if (channel_ != nullptr) { + g_io_channel_unref(channel_); + channel_ = nullptr; + } + + if (wd_ > 0) { + inotify_rm_watch(fd_, wd_); + wd_ = 0; + } + + if (fd_ > 0) { + close(fd_); + fd_ = 0; + } + + disposed_ = true; +} + +bool DbChangeObserver::GetDisposed() { + std::shared_lock s(lock_); + + return disposed_; +} + +bool DbChangeObserver::SetGlobalParserDbPath() { + char* tmp_path = getUserPkgParserDBPathUID(GetGlobalUID()); + if (tmp_path == nullptr) { + LOG(ERROR) << "Fail to global parser db"; + return false; + } + global_parser_db_path_ = tmp_path; + free(tmp_path); + return true; +} + +void DbChangeObserver::RegisterEvent(IEvent* listener) { + std::unique_lock s(lock_); + listener_ = listener; +} + +void DbChangeObserver::UnRegisterEvent() { + std::unique_lock s(lock_); + listener_ = nullptr; +} + +} // namespace pkgmgr_common diff --git a/src/common/db_change_observer.hh b/src/common/db_change_observer.hh new file mode 100644 index 0000000..c6905bf --- /dev/null +++ b/src/common/db_change_observer.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#ifndef COMMON_DB_CHANGE_OBSERVER_HH_ +#define COMMON_DB_CHANGE_OBSERVER_HH_ + +namespace pkgmgr_common { + +#ifndef EXPORT_API +#define EXPORT_API __attribute__((visibility("default"))) +#endif + +class EXPORT_API DbChangeObserver { + public: + class IEvent { + public: + virtual ~IEvent() = default; + virtual void OnDbChanged() = 0; + }; + static DbChangeObserver& GetInst(); + bool Listen(); + void StopListening(); + bool IsChanged(); + void SetChanged(bool changed); + void RegisterEvent(IEvent* listener); + void UnRegisterEvent(); + bool GetDisposed(); + + private: + DbChangeObserver(); + ~DbChangeObserver(); + + int fd_ = 0; + int wd_ = 0; + GIOChannel* channel_ = nullptr; + int tag_ = 0; + bool changed_ = false; + bool disposed_ = true; + std::shared_mutex lock_; + std::string global_parser_db_path_; + IEvent* listener_ = nullptr; + + void Dispose(); + bool SetGlobalParserDbPath(); + static gboolean OnReceiveEvent(GIOChannel* channel, GIOCondition cond, + gpointer user_data); +}; + +} // namespace pkgmgr_common + +#endif // COMMON_DB_CHANGE_OBSERVER_HH_ diff --git a/src/common/request_type.cc b/src/common/request_type.cc index e80c500..da674ad 100644 --- a/src/common/request_type.cc +++ b/src/common/request_type.cc @@ -41,4 +41,13 @@ const char* ReqTypeToString(ReqType type) { return convertArray[type]; } +bool IsDbWriteRequest(ReqType type) { + if (type == pkgmgr_common::ReqType::SET_PKG_INFO || + type == pkgmgr_common::ReqType::SET_CERT_INFO || + type == pkgmgr_common::ReqType::WRITE_QUERY) + return true; + + return false; +} + } // namespace pkgmgr_common diff --git a/src/common/request_type.hh b/src/common/request_type.hh index 5118271..4e7f62b 100644 --- a/src/common/request_type.hh +++ b/src/common/request_type.hh @@ -44,6 +44,8 @@ enum ReqType { EXPORT_API const char* ReqTypeToString(ReqType type); +EXPORT_API bool IsDbWriteRequest(ReqType type); + } // namespace pkgmgr_common #endif // COMMON_REQUEST_TYPE_HH_ diff --git a/src/server/runner.cc b/src/server/runner.cc index db909a0..2d29a93 100644 --- a/src/server/runner.cc +++ b/src/server/runner.cc @@ -60,6 +60,8 @@ Runner::Runner(unsigned int thread_num) { sid_ = g_unix_fd_add(server_->GetFd(), condition, OnReceiveRequest, this); default_uid_ = tzplatform_getuid(TZ_SYS_DEFAULT_USER); pkgmgr_common::SystemLocale::GetInst().RegisterEvent(this); + pkgmgr_common::DbChangeObserver::GetInst().Listen(); + pkgmgr_common::DbChangeObserver::GetInst().RegisterEvent(this); thread_pool_->SetLocale(pkgmgr_common::SystemLocale::GetInst().Get()); if (CacheFlag::SetPreparing()) @@ -70,8 +72,11 @@ Runner::Runner(unsigned int thread_num) { Runner::~Runner() { g_source_remove(sid_); + if (timer_ > 0) + g_source_remove(timer_); CynaraChecker::GetInst().Fini(); pkgmgr_common::SystemLocale::GetInst().UnRegisterEvent(); + pkgmgr_common::DbChangeObserver::GetInst().UnRegisterEvent(); } int Runner::OnReceiveRequest(int fd, GIOCondition cond, void* user_data) { @@ -98,6 +103,11 @@ void Runner::OnChanged(const std::string& locale) { QueueRequest(std::make_shared(default_uid_)); } +void Runner::OnDbChanged() { + QueueRequest(std::make_shared(default_uid_)); + SetCreateCacheTimer(); +} + bool Runner::QueueRequest(std::shared_ptr req) { thread_pool_->PushQueue(std::move(req)); return true; @@ -105,7 +115,10 @@ bool Runner::QueueRequest(std::shared_ptr req) { void Runner::OnCreateCacheDone(bool success) { static bool ready = false; - if (success && !ready) { + if (ready) + return; + + if (success) { ready = true; LOG(WARNING) << "pkginfo-server is ready"; g_idle_add( @@ -118,6 +131,9 @@ void Runner::OnCreateCacheDone(bool success) { return G_SOURCE_REMOVE; }, nullptr); + } else { + LOG(WARNING) << "Fail to create cache"; + SetCreateCacheTimer(); } } @@ -132,4 +148,27 @@ void Runner::SetCPUInheritance() { LOG(ERROR) << "Fail to register cpu inheritance destination ret : " << ret; } +void Runner::SetCreateCacheTimer() { + if (timer_ > 0) + g_source_remove(timer_); + + timer_ = g_timeout_add_seconds_full(G_PRIORITY_LOW, 10, + RetryCreateCache, this, NULL); +} + +gboolean Runner::RetryCreateCache(void* data) { + LOG(WARNING) << "Retry to create cache"; + auto* runner = static_cast(data); + runner->timer_ = 0; + + pkgmgr_common::DbChangeObserver::GetInst().SetChanged(false); + pkgmgr_common::DbChangeObserver::GetInst().Listen(); + + if (CacheFlag::SetPreparing()) { + runner->QueueRequest( + std::make_shared(runner->default_uid_, runner)); + } + return G_SOURCE_REMOVE; +} + } // namespace pkgmgr_server diff --git a/src/server/runner.hh b/src/server/runner.hh index 157531c..6622f58 100644 --- a/src/server/runner.hh +++ b/src/server/runner.hh @@ -25,6 +25,7 @@ #include #include "create_cache_request.hh" +#include "db_change_observer.hh" #include "server_socket.hh" #include "system_locale.hh" #include "worker_thread.hh" @@ -36,6 +37,7 @@ namespace pkgmgr_server { #endif class EXPORT_API Runner : public pkgmgr_common::SystemLocale::IEvent, + public pkgmgr_common::DbChangeObserver::IEvent, public CreateCacheRequest::IEvent { public: explicit Runner(unsigned int thread_num); @@ -43,12 +45,15 @@ class EXPORT_API Runner : public pkgmgr_common::SystemLocale::IEvent, bool QueueRequest(std::shared_ptr req); void OnChanged(const std::string& locale) override; + void OnDbChanged() override; void OnCreateCacheDone(bool success) override; private: static int OnReceiveRequest(int fd, GIOCondition cond, void* user_data); bool QueueRequest(int client_fd); void SetCPUInheritance(); + void SetCreateCacheTimer(); + static gboolean RetryCreateCache(void* data); private: int sid_; @@ -56,6 +61,7 @@ class EXPORT_API Runner : public pkgmgr_common::SystemLocale::IEvent, uid_t default_uid_; std::unique_ptr server_; std::unique_ptr thread_pool_; + guint timer_ = 0; }; } // namespace pkgmgr_server diff --git a/src/server/worker_thread.cc b/src/server/worker_thread.cc index 5e50517..b623a2c 100644 --- a/src/server/worker_thread.cc +++ b/src/server/worker_thread.cc @@ -24,6 +24,7 @@ #include "abstract_parcelable.hh" #include "cynara_checker.hh" +#include "db_change_observer.hh" #include "request_handler_factory.hh" #include "server/database/db_handle_provider.hh" #include "server/database/update_pending_cache_handler.hh" @@ -127,6 +128,8 @@ void WorkerThread::Run() { } pkgmgr_common::ReqType type = req->GetRequestType(); + if (pkgmgr_common::IsDbWriteRequest(type)) + StopDbChangeListening(); std::vector privileges = GetPrivileges(type); if (!CynaraChecker::GetInst().CheckPrivilege(this, req, privileges)) continue; @@ -209,4 +212,14 @@ void WorkerThread::SendError(const std::shared_ptr& req) { req->SendData(p); } +void WorkerThread::StopDbChangeListening() { + auto& db_observer = pkgmgr_common::DbChangeObserver::GetInst(); + + if (db_observer.GetDisposed()) + return; + + LOG(WARNING) << "Try stop listening db change"; + db_observer.StopListening(); +} + } // namespace pkgmgr_server diff --git a/src/server/worker_thread.hh b/src/server/worker_thread.hh index c5ebcb2..7c2aae0 100644 --- a/src/server/worker_thread.hh +++ b/src/server/worker_thread.hh @@ -49,6 +49,7 @@ class EXPORT_API WorkerThread { void Run(); void SendError(const std::shared_ptr& req); void SetMemoryTrimTimer(); + void StopDbChangeListening(); static gboolean TrimMemory(void* data); std::shared_ptr PopQueue();