Implement unified recovery feature 03/234403/13
authorJunghyun Yeon <jungh.yeon@samsung.com>
Tue, 26 May 2020 06:14:46 +0000 (15:14 +0900)
committerJunghyun Yeon <jungh.yeon@samsung.com>
Wed, 3 Jun 2020 02:33:43 +0000 (02:33 +0000)
Unified recovery file will be parsed at pkg recovery tool and
passed into unified backend to handle multi recovery scenario.

Change-Id: I50d7b4b89af0864fde2e137677900aff1b3d5c6b
Signed-off-by: Junghyun Yeon <jungh.yeon@samsung.com>
14 files changed:
CMakeLists.txt
src/common/app_installer.h
src/common/global_recovery_file.cc [new file with mode: 0644]
src/common/global_recovery_file.h [new file with mode: 0644]
src/common/installer_context.h
src/common/installer_runner.cc
src/common/installer_runner.h
src/common/pkgmgr_interface.cc
src/common/pkgmgr_interface.h
src/common/step/configuration/step_configure.cc
src/common/step/recovery/step_create_recovery_file.cc
src/common/step/recovery/step_open_recovery_file.cc
src/common/step/recovery/step_recovery.cc
src/pkg_recovery/pkg_recovery.cc

index 8af046e72a5f729695241ce8fcafcb46c3d830b3..889b8277a436b768fa48af098b2a9928b5da02e3 100644 (file)
@@ -15,12 +15,12 @@ SET(EXTRA_FLAGS "-Wall -Wextra")
 SET(EXTRA_OPTIMIZATION_FLAGS "-Wl,--gc-sections -flto -fmerge-all-constants")
 
 SET(CMAKE_C_FLAGS_PROFILING    "-O2 ${EXTRA_FLAGS}")
-SET(CMAKE_CXX_FLAGS_PROFILING  "-O2 -std=c++11 ${EXTRA_FLAGS}")
+SET(CMAKE_CXX_FLAGS_PROFILING  "-O2 -std=c++14 ${EXTRA_FLAGS}")
 SET(CMAKE_C_FLAGS_DEBUG        "-O0 -g ${EXTRA_FLAGS}")
-SET(CMAKE_CXX_FLAGS_DEBUG      "-O0 -std=c++11 -g ${EXTRA_FLAGS}")
+SET(CMAKE_CXX_FLAGS_DEBUG      "-O0 -std=c++14 -g ${EXTRA_FLAGS}")
 SET(CMAKE_C_FLAGS_RELEASE      "-Os  ${EXTRA_OPTIMIZATION_FLAGS} ${EXTRA_FLAGS}")
-SET(CMAKE_CXX_FLAGS_RELEASE    "-Os -std=c++11 ${EXTRA_OPTIMIZATION_FLAGS} ${EXTRA_FLAGS}")
-SET(CMAKE_CXX_FLAGS_CCOV       "-O0 -std=c++11 -g --coverage ${EXTRA_FLAGS}")
+SET(CMAKE_CXX_FLAGS_RELEASE    "-Os -std=c++14 ${EXTRA_OPTIMIZATION_FLAGS} ${EXTRA_FLAGS}")
+SET(CMAKE_CXX_FLAGS_CCOV       "-O0 -std=c++14 -g --coverage ${EXTRA_FLAGS}")
 
 # Linker flags
 SET(EXTRA_LINKER_FLAGS "-Wl,--as-needed ${EXTRA_OPTIMIZATION_FLAGS} -fwhole-program")
index 8e7baeb1a75286ad464ac63297f88d063685fdfc..add57c34d548222a06f3ff1434dcf01f7c50fb3e 100644 (file)
@@ -305,6 +305,8 @@ class AppInstaller {
 
   int index_;
 
+  friend class InstallerRunner;
+
   SCOPE_LOG_TAG(AppInstaller)
 
   DISALLOW_COPY_AND_ASSIGN(AppInstaller);
diff --git a/src/common/global_recovery_file.cc b/src/common/global_recovery_file.cc
new file mode 100644 (file)
index 0000000..ae6da34
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#include "common/global_recovery_file.h"
+
+#include <boost/filesystem/path.hpp>
+
+#include <manifest_parser/utils/logging.h>
+
+#include <string>
+
+#include "common/utils/file_util.h"
+#include "common/pkgmgr_interface.h"
+
+namespace bf = boost::filesystem;
+namespace ci = common_installer;
+
+namespace {
+
+const char kGlobalTypeName[] = "unified";
+
+bf::path Backup(const bf::path& filepath) {
+  bf::path backup_path(filepath);
+  backup_path += ".bck";
+
+  if (!ci::MoveFile(filepath, backup_path) ||
+      !ci::CopyFile(backup_path, filepath)) {
+    LOG(ERROR) << "Failed to backup";
+    return {};
+  }
+
+  return backup_path;
+}
+
+}  // namespace
+
+namespace common_installer {
+
+GlobalRecoveryFile::GlobalRecoveryFile(
+    std::shared_ptr<PkgMgrInterface> pkgmgr) : pkgmgr_(pkgmgr) {
+}
+
+GlobalRecoveryFile::~GlobalRecoveryFile() {
+  if (bf::exists(recovery_filepath_))
+    ci::Remove(recovery_filepath_);
+  if (bf::exists(backup_path_))
+    ci::Remove(backup_path_);
+}
+
+
+bool GlobalRecoveryFile::Init() {
+  boost::filesystem::path root_path = ci::GetRootAppPath(
+        pkgmgr_->GetIsPreloadRequest(), pkgmgr_->GetUid());
+  recovery_filepath_ = GenerateRecoveryFilePath(root_path, kGlobalTypeName);
+  if (recovery_filepath_.empty())
+      return false;
+
+  return true;
+}
+
+std::string GlobalRecoveryFile::AddPathWithType(
+    const std::string& pkg_type) {
+  boost::filesystem::path root_path = ci::GetRootAppPath(
+          pkgmgr_->GetIsPreloadRequest(), pkgmgr_->GetUid());
+  bf::path recovery_filepath = GenerateRecoveryFilePath(root_path, pkg_type);
+  if (!AppendString(recovery_filepath.string()))
+    return {};
+
+  recovery_list_.emplace_back(recovery_filepath.string());
+  return recovery_filepath.string();
+}
+
+bool GlobalRecoveryFile::AppendPath(
+    const bf::path& additional_path) {
+  return AppendString(additional_path.string());
+}
+
+bool GlobalRecoveryFile::AppendCleanUp() {
+  return AppendString("cleanup");
+}
+
+bool GlobalRecoveryFile::AppendString(const std::string& val) {
+  if (recovery_filepath_.empty())
+    return true;
+
+  if (bf::exists(recovery_filepath_)) {
+    backup_path_ = Backup(recovery_filepath_);
+    if (backup_path_.empty()) {
+      LOG(ERROR) << "Failed to backup";
+      return false;
+    }
+  }
+
+  FILE* handle = fopen(recovery_filepath_.c_str(), "a");
+  if (!handle) {
+    LOG(ERROR) << "Failed to open file :" << recovery_filepath_;
+    return false;
+  }
+
+  fputs(val.c_str(), handle);
+  fputs("\n", handle);
+  fclose(handle);
+  sync();
+
+  ci::Remove(backup_path_);
+  return true;
+}
+
+bf::path GlobalRecoveryFile::GenerateRecoveryFilePath(
+    const bf::path& path, const std::string& type) {
+  bf::path pattern = path;
+  pattern += "/";
+  pattern += type;
+  pattern += "-recovery-%%%%%%";
+  bf::path tmp_path;
+
+  std::vector<std::string>::iterator iter;
+  do {
+    tmp_path = boost::filesystem::unique_path(pattern);
+    iter = std::find(recovery_list_.begin(), recovery_list_.end(),
+        tmp_path.string());
+  } while (boost::filesystem::exists(tmp_path) ||
+      iter != recovery_list_.end());
+
+  return tmp_path;
+}
+
+}  // namespace common_installer
diff --git a/src/common/global_recovery_file.h b/src/common/global_recovery_file.h
new file mode 100644 (file)
index 0000000..da3e78a
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef COMMON_GLOBAL_RECOVERY_FILE_H_
+#define COMMON_GLOBAL_RECOVERY_FILE_H_
+
+#include <boost/filesystem/path.hpp>
+
+#include <string>
+#include <vector>
+
+#include "common/pkgmgr_interface.h"
+
+namespace bf = boost::filesystem;
+
+namespace common_installer {
+
+class GlobalRecoveryFile {
+ public:
+  explicit GlobalRecoveryFile(std::shared_ptr<PkgMgrInterface> pkgmgr);
+  ~GlobalRecoveryFile();
+
+  bool Init();
+
+  std::string AddPathWithType(const std::string& pkg_type);
+
+  bool AppendPath(const bf::path& additional_path);
+
+  bool AppendCleanUp();
+
+ private:
+  bool AppendString(const std::string& val);
+  bf::path GenerateRecoveryFilePath(
+      const bf::path& path,
+      const std::string& type);
+  std::shared_ptr<common_installer::PkgMgrInterface> pkgmgr_;
+  bf::path recovery_filepath_;
+  bf::path backup_path_;
+  std::vector<std::string> recovery_list_;
+};
+
+}  // namespace common_installer
+
+#endif  // COMMON_GLOBAL_RECOVERY_FILE_H_
index 374127b7f9daa38d6d464a7990ea05e1314ebead..42b45dd945c28405c7245e944f853d184333b8cb 100644 (file)
@@ -109,11 +109,28 @@ class RecoveryInfo {
    * \param rf RecoveryFile object (pointer to object)
    */
   explicit RecoveryInfo(std::unique_ptr<recovery::RecoveryFile> rf)
-      : recovery_file(std::move(rf)) {
+      : recovery_file(std::move(rf)), cleanup(false) {
+  }
+
+  /**
+   * Constructor.
+   *
+   * \param path path of recovery file to be created
+   * \param recovery_cleanup path of recovery file to be created
+   */
+  explicit RecoveryInfo(const boost::filesystem::path& path,
+      bool recovery_cleanup = false)
+      : filepath(path), cleanup(recovery_cleanup) {
   }
 
   /** pointer to RecoveryFile */
   std::unique_ptr<recovery::RecoveryFile> recovery_file;
+
+  /** path of recovery file to be created */
+  boost::filesystem::path filepath;
+
+  /** cleanup flag delivered by global recovery file */
+  bool cleanup;
 };
 
 /**
index 9762fd2cf19fc8a7ca960754ec90f2154dfda3ce..7d2cfd529a7bdfe39f897137280b249473cb9f59 100644 (file)
@@ -7,9 +7,11 @@
 #include <manifest_parser/utils/logging.h>
 
 #include <list>
+#include <string>
 #include <utility>
 
 #include "common/app_installer.h"
+#include "common/installer_context.h"
 #include "common/installer_factory.h"
 #include "common/pkgmgr_interface.h"
 
@@ -61,6 +63,9 @@ bool InstallerRunner::SortInstallers() {
 
 AppInstaller::Result InstallerRunner::Run() {
   AppInstaller::Result result = AppInstaller::Result::OK;
+  if (installers_.size() == 0)
+    return result;
+
   if (getuid() != 0 && NeedCheckDependency()) {
     if (!SortInstallers()) {
       if (pi_) {
@@ -87,6 +92,8 @@ AppInstaller::Result InstallerRunner::Run() {
         result = AppInstaller::Result::UNDO_ERROR;
     } while (it-- != installers_.begin());
   } else {
+    if (pkgmgr_->GetRequestType() != RequestType::Recovery)
+      global_recovery_->AppendCleanUp();
     --it;
     do {
       // Clean operation always succeeds
@@ -98,9 +105,23 @@ AppInstaller::Result InstallerRunner::Run() {
 }
 
 void InstallerRunner::Init() {
+  global_recovery_ = std::make_unique<GlobalRecoveryFile>(pkgmgr_);
+  bool is_global_request = (pkgmgr_->GetRequestInfoCount() > 1) ? true : false;
+  if (pkgmgr_->GetRequestType() != RequestType::Recovery &&
+        is_global_request) {
+    if (!global_recovery_->Init())
+      return;
+  }
   for (int i = 0; i < pkgmgr_->GetRequestInfoCount(); i++) {
     std::unique_ptr<AppInstaller> installer =
         factory_->CreateInstaller(pkgmgr_, i);
+    if (pkgmgr_->GetRequestType() != RequestType::Recovery &&
+        is_global_request) {
+      std::string recovery_filepath =
+          global_recovery_->AddPathWithType(
+              installer->context_->pkg_type.get());
+      installer->context_->recovery_info.set(RecoveryInfo(recovery_filepath));
+    }
     installers_.emplace_back(std::move(installer));
   }
 }
index 2d97991a31abaff3b158784bc2ad43ec24824da4..df404ae8731d86adfa2f1e6214d8018f22c2809c 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "common/app_installer.h"
 #include "common/dependency_checker.h"
+#include "common/global_recovery_file.h"
 #include "common/pkgmgr_interface.h"
 
 namespace common_installer {
@@ -32,6 +33,7 @@ class InstallerRunner {
   PkgMgrPtr pkgmgr_;
   std::list<std::unique_ptr<AppInstaller>> installers_;
   std::unique_ptr<PkgmgrSignal> pi_;
+  std::unique_ptr<GlobalRecoveryFile> global_recovery_;
 };
 
 }  // namespace common_installer
index b95d2a7f2e8a9e33eebff79aa39bc45e59731bbb..1afbb673bfb0a2b884a45624e672dcf0a25d5dc4 100644 (file)
@@ -146,6 +146,24 @@ void PkgMgrInterface::AddAppQueryInterface(
 }
 
 RequestType PkgMgrInterface::GetRequestType(int idx) const {
+  // These type could be determined even if there are no query_interface_.
+  switch (pkgmgr_installer_get_request_type(pi_)) {
+    case PKGMGR_REQ_DISABLE_PKG:
+      return RequestType::DisablePkg;
+    case PKGMGR_REQ_ENABLE_PKG:
+      return RequestType::EnablePkg;
+    case PKGMGR_REQ_MIGRATE_EXTIMG:
+      return RequestType::MigrateExtImg;
+    case PKGMGR_REQ_RECOVER_DB:
+      return RequestType::RecoverDB;
+    case PKGMGR_REQ_REINSTALL:
+      return RequestType::Reinstall;
+    case PKGMGR_REQ_RECOVER:
+      return RequestType::Recovery;
+    case PKGMGR_REQ_MOVE:
+      return RequestType::Move;
+  }
+
   AppQueryInterface* query_interface = query_interface_;
   if (!query_interface) {
     auto it = query_interface_map_.find(idx);
@@ -209,12 +227,6 @@ RequestType PkgMgrInterface::GetRequestType(int idx) const {
       else
         return RequestType::Uninstall;
     }
-    case PKGMGR_REQ_REINSTALL:
-      return RequestType::Reinstall;
-    case PKGMGR_REQ_RECOVER:
-      return RequestType::Recovery;
-    case PKGMGR_REQ_MOVE:
-      return RequestType::Move;
     case PKGMGR_REQ_MANIFEST_DIRECT_INSTALL:
       if (!is_app_installed_.get()) {
         if (GetIsPreloadRequest() && GetIsPartialRW())
@@ -232,14 +244,6 @@ RequestType PkgMgrInterface::GetRequestType(int idx) const {
         return RequestType::MountInstall;
       else
         return RequestType::MountUpdate;
-    case PKGMGR_REQ_DISABLE_PKG:
-      return RequestType::DisablePkg;
-    case PKGMGR_REQ_ENABLE_PKG:
-      return RequestType::EnablePkg;
-    case PKGMGR_REQ_MIGRATE_EXTIMG:
-      return RequestType::MigrateExtImg;
-    case PKGMGR_REQ_RECOVER_DB:
-      return RequestType::RecoverDB;
     default:
       return RequestType::Unknown;
   }
@@ -322,4 +326,8 @@ int PkgMgrInterface::GetRequestInfoCount() const {
   return pkgmgr_installer_get_request_info_count(pi_);
 }
 
+bool PkgMgrInterface::GetRecoveryCleanup() const {
+  return (pkgmgr_installer_get_recovery_cleanup(pi_) == 1);
+}
+
 }  // namespace common_installer
index 6b68ff9f1bf4b736ce218142a20a631995ef09a8..a090c34c44c9db4425e28bbc990744098c2800eb 100644 (file)
@@ -222,6 +222,13 @@ class PkgMgrInterface {
    */
   int GetRequestInfoCount() const;
 
+  /**
+   * Returns whether recovery mode is set to cleanup or not.
+   *
+   * \return True if recovery mode is set to cleanup. Otherwise, return false.
+   */
+  bool GetRecoveryCleanup() const;
+
   /**
    * Get Raw pointer to pkgmgr_installer object
    * NOTE: It should not be used (PkgMgrInterface can destroy it
index cf3f72af166a4a498962197179aaee69eae4910e..2961426dbf1bf6668e641287fc53d3d1cb93f66f 100644 (file)
@@ -83,6 +83,9 @@ Step::Status StepConfigure::process() {
       break;
     case RequestType::Recovery:
       context_->file_path.set(pkgmgr_->GetRequestInfo(context_->index.get()));
+      context_->recovery_info.set(
+          RecoveryInfo(context_->file_path.get(),
+              pkgmgr_->GetRecoveryCleanup()));
       break;
     case RequestType::MountInstall:
     case RequestType::MountUpdate:
index 62a2508f1b051f93c55c70413175dc33c6e235e0..c5d1236536de8a1d91f9a47b74ab248ee42682c4 100644 (file)
@@ -28,17 +28,26 @@ namespace common_installer {
 namespace recovery {
 
 Step::Status StepCreateRecoveryFile::process() {
-  std::string recovery_filename = context_->pkg_type.get() + "-recovery";
-  bf::path recovery_filepath = GenerateTemporaryPath(
-      context_->root_application_path.get() / recovery_filename);
+  bf::path recovery_filename = context_->recovery_info.get().filepath;
+
+  if (recovery_filename.empty()) {
+    std::string file_format = context_->pkg_type.get() + "-recovery";
+    recovery_filename = GenerateTemporaryPath(
+        context_->root_application_path.get() / file_format);
+  }
+
   auto recovery_file =
-      recovery::RecoveryFile::CreateRecoveryFile(recovery_filepath,
-          context_->request_type.get());
+    recovery::RecoveryFile::CreateRecoveryFile(recovery_filename,
+        context_->request_type.get());
   if (!recovery_file) {
     LOG(ERROR) << "Failed to create recovery file";
     return Status::CONFIG_ERROR;
   }
-  context_->recovery_info.set(RecoveryInfo(std::move(recovery_file)));
+
+  if (context_->recovery_info.get().filepath.empty())
+    context_->recovery_info.set(RecoveryInfo(std::move(recovery_file)));
+  else
+    context_->recovery_info.get().recovery_file = std::move(recovery_file);
 
   return Status::OK;
 }
index dded54a5d466f5562977fc5bc341b2bd694196cc..bb25fbdbc260e39a772a4e4c78dc1568d43e2f0b 100644 (file)
@@ -59,7 +59,13 @@ Step::Status StepOpenRecoveryFile::process() {
   if (recovery_file->cleanup())
     LOG(INFO) << "Running cleanup operation";
 
+  bool global_recovery_cleanup = context_->recovery_info.get().cleanup;
   context_->recovery_info.set(RecoveryInfo(std::move(recovery_file)));
+  if (global_recovery_cleanup) {
+    LOG(INFO) << "Set recovery cleanup";
+    context_->recovery_info.get().cleanup = true;
+  }
+
   return Status::OK;
 }
 
index a24d844370f422815ce72cca3677bbbdb304c99f..a1cd145361f038e111094c6287affe8568af3ab1 100644 (file)
@@ -12,7 +12,8 @@ namespace common_installer {
 namespace recovery {
 
 Step::Status StepRecovery::process() {
-  if (context_->recovery_info.get().recovery_file->cleanup())
+  if (context_->recovery_info.get().recovery_file->cleanup() ||
+      context_->recovery_info.get().cleanup)
     return Cleanup();
 
   switch (context_->recovery_info.get().recovery_file->type()) {
index 72babe0c61b16c313cc616c2a03566e6bcfee28e..18a3448a81ff71da0191649c44099696c8b86277 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <glib.h>
 #include <common/request.h>
+#include <common/utils/file_util.h>
 #include <common/utils/subprocess.h>
 #include <common/utils/user_util.h>
 #include <manifest_parser/utils/logging.h>
@@ -29,6 +30,40 @@ const char kRecoveryFilePattern[] = "^(.*)-recovery-(.){6}$";
 const char kBackupFilePattern[] = "^(.*)-recovery-(.){6}\\.bck$";
 const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
 
+std::string TruncateNewLine(const char* data) {
+  int length = strlen(data);
+  if (data[length - 1] == '\n')
+      --length;
+  return std::string(data, length);
+}
+
+std::vector<std::string> ParseRecoveryFile(const char* file) {
+  FILE* handle = fopen(file, "r");
+  if (!handle) {
+    LOG(ERROR) << "Failed to open recovery file :" << file;
+    return {};
+  }
+
+  std::vector<std::string> arguments;
+  std::array<char, 200> data;
+  data[0] = '\0';
+  while (fgets(data.data(), data.size(), handle)) {
+    std::string line_data = TruncateNewLine(data.data());
+    if (line_data == "cleanup") {
+      arguments.emplace_back("--recovery-cleanup");
+      break;
+    }
+
+    if (!boost::filesystem::exists(line_data))
+      continue;
+
+    arguments.emplace_back(line_data);
+  }
+  fclose(handle);
+
+  return arguments;
+}
+
 class PkgRecoveryService {
  public:
   PkgRecoveryService();
@@ -53,10 +88,25 @@ bool PkgRecoveryService::RunBackend(uid_t uid, const char* type,
   std::string backend_cmd = "/usr/bin/" + std::string(type) + "-backend";
   ci::Subprocess backend(backend_cmd);
   std::string str_uid = std::to_string(uid);
-  backend.Run("-b", file, "-u", str_uid.c_str());
+  if (std::string(type) == "unified") {
+    auto arguments = ParseRecoveryFile(file);
+    if (!arguments.size())
+      return ci::Remove(bf::path(file));
+
+    arguments.emplace(arguments.begin(), "-b");
+    arguments.emplace_back("-u");
+    arguments.emplace_back(str_uid.c_str());
+
+    backend.RunWithArgs(arguments);
+  } else {
+    backend.Run("-b", file, "-u", str_uid.c_str());
+  }
   int status = backend.Wait();
   if (WIFSIGNALED(status) || WEXITSTATUS(status))
     return false;
+
+  ci::Remove(bf::path(file));
+
   return true;
 }
 
@@ -116,7 +166,10 @@ std::vector<RecoverEntry> PkgRecoveryService::SearchRecoveryFiles(uid_t uid) {
       if (std::regex_search(file, match, recovery_regex)) {
         LOG(INFO) << "Found recovery file: " << file;
         std::string type(match[1]);
-        list.emplace_back(type, iter->path().string());
+        if (type == "unified")
+          list.emplace(list.begin(), type, iter->path().string());
+        else
+          list.emplace_back(type, iter->path().string());
       }
     } catch (...) {
       LOG(WARNING) << "Exception occurred: "
@@ -134,6 +187,9 @@ void PkgRecoveryService::ProcessRecovery(uid_t uid,
   for (auto entry : entries) {
     const char* type = entry.first.c_str();
     const char* file = entry.second.c_str();
+    if (!bf::exists(file))
+      continue;
+
     if (!RunBackend(uid, type, file))
       LOG(ERROR) << "Recovery process for " << file << " failed";
   }