Fix inconsistency between cache and database 81/302481/5
authorIlho Kim <ilho159.kim@samsung.com>
Thu, 7 Dec 2023 12:40:49 +0000 (21:40 +0900)
committerIlho Kim <ilho159.kim@samsung.com>
Fri, 8 Dec 2023 00:55:18 +0000 (09:55 +0900)
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 <ilho159.kim@samsung.com>
src/common/db_change_observer.cc [new file with mode: 0644]
src/common/db_change_observer.hh [new file with mode: 0644]
src/common/request_type.cc
src/common/request_type.hh
src/server/runner.cc
src/server/runner.hh
src/server/worker_thread.cc
src/server/worker_thread.hh

diff --git a/src/common/db_change_observer.cc b/src/common/db_change_observer.cc
new file mode 100644 (file)
index 0000000..72ddb9d
--- /dev/null
@@ -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 <tzplatform_config.h>
+
+#include <utility>
+
+#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<DbChangeObserver*>(user_data);
+  int fd = g_io_channel_unix_get_fd(channel);
+
+  while(read(fd, buf, sizeof(buf)) > 0);
+
+  std::unique_lock<std::shared_mutex> 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<std::shared_mutex> u(lock_);
+  SetGlobalParserDbPath();
+}
+
+DbChangeObserver::~DbChangeObserver() {
+  std::unique_lock<std::shared_mutex> u(lock_);
+  Dispose();
+}
+
+bool DbChangeObserver::Listen() {
+  std::unique_lock<std::shared_mutex> 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<std::shared_mutex> u(lock_);
+
+  Dispose();
+}
+
+bool DbChangeObserver::IsChanged() {
+  std::shared_lock<std::shared_mutex> s(lock_);
+  return changed_;
+}
+
+void DbChangeObserver::SetChanged(bool changed) {
+  std::unique_lock<std::shared_mutex>s (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<std::shared_mutex> 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<std::shared_mutex> s(lock_);
+  listener_ = listener;
+}
+
+void DbChangeObserver::UnRegisterEvent() {
+  std::unique_lock<std::shared_mutex> 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 (file)
index 0000000..c6905bf
--- /dev/null
@@ -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 <gio/gio.h>
+#include <glib.h>
+#include <sys/inotify.h>
+
+#include <string>
+#include <shared_mutex>
+
+#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_
index e80c500..da674ad 100644 (file)
@@ -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
index 5118271..4e7f62b 100644 (file)
@@ -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_
index db909a0..2d29a93 100644 (file)
@@ -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<RemoveAllCacheRequest>(default_uid_));
 }
 
+void Runner::OnDbChanged() {
+  QueueRequest(std::make_shared<RemoveAllCacheRequest>(default_uid_));
+  SetCreateCacheTimer();
+}
+
 bool Runner::QueueRequest(std::shared_ptr<PkgRequest> req) {
   thread_pool_->PushQueue(std::move(req));
   return true;
@@ -105,7 +115,10 @@ bool Runner::QueueRequest(std::shared_ptr<PkgRequest> 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<Runner*>(data);
+  runner->timer_ = 0;
+
+  pkgmgr_common::DbChangeObserver::GetInst().SetChanged(false);
+  pkgmgr_common::DbChangeObserver::GetInst().Listen();
+
+  if (CacheFlag::SetPreparing()) {
+    runner->QueueRequest(
+        std::make_shared<CreateCacheRequest>(runner->default_uid_, runner));
+  }
+  return G_SOURCE_REMOVE;
+}
+
 }  // namespace pkgmgr_server
index 157531c..6622f58 100644 (file)
@@ -25,6 +25,7 @@
 #include <unordered_map>
 
 #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<PkgRequest> 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<pkgmgr_common::socket::ServerSocket> server_;
   std::unique_ptr<WorkerThread> thread_pool_;
+  guint timer_ = 0;
 };
 
 }  // namespace pkgmgr_server
index 5e50517..b623a2c 100644 (file)
@@ -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<std::string> privileges = GetPrivileges(type);
       if (!CynaraChecker::GetInst().CheckPrivilege(this, req, privileges))
         continue;
@@ -209,4 +212,14 @@ void WorkerThread::SendError(const std::shared_ptr<PkgRequest>& 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
index c5ebcb2..7c2aae0 100644 (file)
@@ -49,6 +49,7 @@ class EXPORT_API WorkerThread {
   void Run();
   void SendError(const std::shared_ptr<PkgRequest>& req);
   void SetMemoryTrimTimer();
+  void StopDbChangeListening();
   static gboolean TrimMemory(void* data);
   std::shared_ptr<PkgRequest> PopQueue();