Add pkg_recovery tools 28/117128/1
authorSangyoon Jang <s89.jang@samsung.com>
Fri, 10 Jun 2016 04:34:21 +0000 (13:34 +0900)
committerSangyoon Jang <s89.jang@samsung.com>
Fri, 3 Mar 2017 02:24:24 +0000 (11:24 +0900)
Two new binary will be introduced:
 1. pkg_recovery
  This tool activated with dbus call and launch backend for recovery process.
 2. pkg_recovery_helper
  This tool will be launched when the user session is started.
  Search recovery files at $HOME/apps_rw and launch pkg_recovery tool by
  dbus for actual recovery process.

For working above new tools, recovry file naming rule is changed to:
<type>-recovery-<tmpstr>

Change-Id: Iec84e4163ae0addd87443bac3d2154b872017d4e
Signed-off-by: Sangyoon Jang <s89.jang@samsung.com>
CMakeLists.txt
packaging/app-installers.spec
src/CMakeLists.txt
src/common/step/configuration/step_configure.cc
src/pkg_recovery/CMakeLists.txt [new file with mode: 0644]
src/pkg_recovery/org.tizen.pkg_recovery.conf [new file with mode: 0644]
src/pkg_recovery/org.tizen.pkg_recovery.service [new file with mode: 0644]
src/pkg_recovery/package-recovery-helper.service [new file with mode: 0644]
src/pkg_recovery/package-recovery.service [new file with mode: 0644]
src/pkg_recovery/pkg_recovery.cc [new file with mode: 0644]
src/pkg_recovery/pkg_recovery_helper.cc [new file with mode: 0644]

index bfb4df8..2754674 100644 (file)
@@ -30,6 +30,8 @@ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EXTRA_LINKER_FLAGS}")
 SET(TARGET_LIBNAME_COMMON "app-installers")
 SET(TARGET_PKGDIR_TOOL "pkgdir-tool")
 SET(TARGET_PKG_INITDB "pkg_initdb")
+SET(TARGET_PKG_RECOVERY "pkg_recovery")
+SET(TARGET_PKG_RECOVERY_HELPER "pkg_recovery_helper")
 
 ADD_DEFINITIONS("-DPROJECT_TAG=\"APP_INSTALLERS\"")
 
index b3f28c4..b5d5317 100644 (file)
@@ -75,12 +75,17 @@ cp %{SOURCE1001} .
 %cmake . -DCMAKE_BUILD_TYPE=%{?build_type:%build_type} \
          -DTIZEN_FULL_VERSION=%{tizen_full_version} \
         -DUNITDIR=%{_unitdir} \
+         -DUNITDIR_USER=%{_unitdir_user} \
         -DADMIN_USER_GLOBAL_INSTALLATION_ONLY:BOOL=${ADMIN_USER_GLOBAL_INSTALLATION_ONLY}
+
 make %{?_smp_mflags}
 
 %install
 %make_install
 
+mkdir -p %{buildroot}%{_unitdir_user}/default.target.wants
+ln -sf ../package-recovery-helper.service %{buildroot}%{_unitdir_user}/default.target.wants/package-recovery-helper.service
+
 %post
 
 %postun
@@ -93,11 +98,17 @@ make %{?_smp_mflags}
 %{_bindir}/pkgdir-tool
 %{_prefix}/share/dbus-1/system-services/org.tizen.pkgdir_tool.service
 %{_sysconfdir}/dbus-1/system.d/org.tizen.pkgdir_tool.conf
-%{_sysconfdir}/dbus-1/system.d/org.tizen.pkgdir_tool.conf
 %{_unitdir}/pkgdir-tool.service
 %{_bindir}/pkg_initdb
 %attr(0755,root,root) %{_sysconfdir}/gumd/useradd.d/10_package-manager-add.post
 %attr(0755,root,root) %{_sysconfdir}/gumd/userdel.d/11_package-manager-del.post
+%{_bindir}/pkg_recovery
+%{_bindir}/pkg_recovery_helper
+%{_prefix}/share/dbus-1/system-services/org.tizen.pkg_recovery.service
+%{_sysconfdir}/dbus-1/system.d/org.tizen.pkg_recovery.conf
+%{_unitdir}/package-recovery.service
+%{_unitdir_user}/package-recovery-helper.service
+%{_unitdir_user}/default.target.wants/package-recovery-helper.service
 %license LICENSE
 
 %files devel
index d442a44..5d3dfbf 100644 (file)
@@ -1,4 +1,5 @@
 ADD_SUBDIRECTORY(common)
 ADD_SUBDIRECTORY(pkg_initdb)
+ADD_SUBDIRECTORY(pkg_recovery)
 ADD_SUBDIRECTORY(pkgdir_tool)
 ADD_SUBDIRECTORY(unit_tests)
index 2e53d9a..7bf5ce6 100644 (file)
@@ -158,7 +158,8 @@ Step::Status StepConfigure::process() {
     std::unique_ptr<recovery::RecoveryFile> recovery_file =
         recovery::RecoveryFile::CreateRecoveryFileForPath(
             GenerateTemporaryPath(
-                context_->root_application_path.get() / "recovery"));
+                context_->root_application_path.get() /
+                (context_->pkg_type.get() + "-recovery")));
     if (!recovery_file) {
       LOG(ERROR) << "Failed to create recovery file";
       return Status::CONFIG_ERROR;
diff --git a/src/pkg_recovery/CMakeLists.txt b/src/pkg_recovery/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5077d4b
--- /dev/null
@@ -0,0 +1,42 @@
+# Target - sources
+SET(PKG_RECOVERY_SRCS
+  pkg_recovery.cc
+)
+SET(PKG_RECOVERY_HELPER_SRCS
+  pkg_recovery_helper.cc
+)
+
+# Target - definition
+ADD_EXECUTABLE(${TARGET_PKG_RECOVERY} ${PKG_RECOVERY_SRCS})
+ADD_EXECUTABLE(${TARGET_PKG_RECOVERY_HELPER} ${PKG_RECOVERY_HELPER_SRCS})
+# Target - includes
+TARGET_INCLUDE_DIRECTORIES(${TARGET_PKG_RECOVERY} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../")
+# Target - deps
+APPLY_PKG_CONFIG(${TARGET_PKG_RECOVERY} PUBLIC
+  GDBUS_DEPS
+  TZPLATFORM_CONFIG_DEPS
+  Boost
+)
+TARGET_INCLUDE_DIRECTORIES(${TARGET_PKG_RECOVERY_HELPER} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../")
+# Target - deps
+APPLY_PKG_CONFIG(${TARGET_PKG_RECOVERY_HELPER} PUBLIC
+  GDBUS_DEPS
+  TZPLATFORM_CONFIG_DEPS
+  Boost
+)
+
+# Target - in-package deps
+TARGET_LINK_LIBRARIES(${TARGET_PKG_RECOVERY} PUBLIC ${TARGET_LIBNAME_COMMON})
+SET_TARGET_PROPERTIES(${TARGET_PKG_RECOVERY} PROPERTIES COMPILE_FLAGS ${CFLAGS} "-fPIE")
+SET_TARGET_PROPERTIES(${TARGET_PKG_RECOVERY} PROPERTIES LINK_FLAGS "-pie")
+TARGET_LINK_LIBRARIES(${TARGET_PKG_RECOVERY_HELPER} PUBLIC ${TARGET_LIBNAME_COMMON})
+SET_TARGET_PROPERTIES(${TARGET_PKG_RECOVERY_HELPER} PROPERTIES COMPILE_FLAGS ${CFLAGS} "-fPIE")
+SET_TARGET_PROPERTIES(${TARGET_PKG_RECOVERY_HELPER} PROPERTIES LINK_FLAGS "-pie")
+
+# Install
+INSTALL(TARGETS ${TARGET_PKG_RECOVERY} DESTINATION ${BINDIR})
+INSTALL(TARGETS ${TARGET_PKG_RECOVERY_HELPER} DESTINATION ${BINDIR})
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.pkg_recovery.service DESTINATION ${PREFIX}/share/dbus-1/system-services/)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.pkg_recovery.conf DESTINATION ${SYSCONF_INSTALL_DIR}/dbus-1/system.d/)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/package-recovery.service DESTINATION ${UNITDIR})
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/package-recovery-helper.service DESTINATION ${UNITDIR_USER})
diff --git a/src/pkg_recovery/org.tizen.pkg_recovery.conf b/src/pkg_recovery/org.tizen.pkg_recovery.conf
new file mode 100644 (file)
index 0000000..ad6b14f
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+    "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<busconfig>
+  <policy user="root">
+    <allow own="org.tizen.pkg_recovery"/>
+  </policy>
+  <policy user="app_fw">
+    <allow own="org.tizen.pkg_recovery"/>
+  </policy>
+  <policy context="default">
+    <check send_destination="org.tizen.pkg_recovery" send_interface="org.tizen.pkg_recovery" privilege="http://tizen.org/privilege/packagemanager.admin"/>
+  </policy>
+</busconfig>
diff --git a/src/pkg_recovery/org.tizen.pkg_recovery.service b/src/pkg_recovery/org.tizen.pkg_recovery.service
new file mode 100644 (file)
index 0000000..5379d9f
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.tizen.pkg_recovery
+Exec=/bin/false
+SystemdService=package-recovery.service
diff --git a/src/pkg_recovery/package-recovery-helper.service b/src/pkg_recovery/package-recovery-helper.service
new file mode 100644 (file)
index 0000000..106cdf4
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Installation Recovery Helper Service
+
+[Service]
+ExecStart=/usr/bin/pkg_recovery_helper
diff --git a/src/pkg_recovery/package-recovery.service b/src/pkg_recovery/package-recovery.service
new file mode 100644 (file)
index 0000000..ecd92dd
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Installation Recovery Service
+Requires=dbus.service
+
+[Service]
+User=app_fw
+Group=app_fw
+ExecStart=/usr/bin/pkg_recovery
diff --git a/src/pkg_recovery/pkg_recovery.cc b/src/pkg_recovery/pkg_recovery.cc
new file mode 100644 (file)
index 0000000..3653123
--- /dev/null
@@ -0,0 +1,195 @@
+// Copyright (c) 2017 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 <glib.h>
+#include <gio/gio.h>
+
+#include <manifest_parser/utils/logging.h>
+#include <common/utils/subprocess.h>
+
+#define UNUSED(expr) (void)(expr)
+
+namespace ci = common_installer;
+
+namespace {
+
+const char kDBusInstropectionXml[] =
+  "<node>"
+  "  <interface name='org.tizen.pkg_recovery'>"
+  "    <method name='RecoverPkg'>"
+  "      <arg type='u' name='uid' direction='in'/>"
+  "      <arg type='s' name='type' direction='in'/>"
+  "      <arg type='s' name='file' direction='in'/>"
+  "      <arg type='b' name='result' direction='out'/>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+const char kDBusServiceName[] = "org.tizen.pkg_recovery";
+const char kDBusObjectPath[] = "/org/tizen/pkg_recovery";
+
+class PkgRecoveryService {
+ public:
+  PkgRecoveryService();
+  ~PkgRecoveryService();
+  bool Init();
+  void Run();
+
+ private:
+  bool RunBackend(uid_t uid, const char* type, const char* file);
+  void Finish();
+  void RenewTimeout(int ms);
+  void HandleMethodCall(GDBusConnection* connection,
+      const gchar* sender, const gchar* object_path,
+      const gchar* interface_name, const gchar* method_name,
+      GVariant* parameters, GDBusMethodInvocation* invocation,
+      gpointer user_data);
+  void OnBusAcquired(GDBusConnection* connection, const gchar* name,
+      gpointer user_data);
+
+  GDBusNodeInfo* node_info_;
+  guint owner_id_;
+  GMainLoop* loop_;
+  guint sid_;
+};
+
+PkgRecoveryService::PkgRecoveryService() :
+    node_info_(nullptr), owner_id_(0), loop_(nullptr), sid_(0) {
+}
+
+PkgRecoveryService::~PkgRecoveryService() {
+  Finish();
+}
+
+bool PkgRecoveryService::Init() {
+  node_info_ = g_dbus_node_info_new_for_xml(kDBusInstropectionXml, nullptr);
+  if (!node_info_) {
+    LOG(ERROR) << "Failed to create DBus node info";
+    return false;
+  }
+  owner_id_ = g_bus_own_name(G_BUS_TYPE_SYSTEM, kDBusServiceName,
+      G_BUS_NAME_OWNER_FLAGS_NONE,
+      [](GDBusConnection* connection, const gchar* name,
+          gpointer user_data) {
+        reinterpret_cast<PkgRecoveryService*>(user_data)->OnBusAcquired(
+            connection, name, user_data);
+      },
+      nullptr, nullptr, this, nullptr);
+
+  loop_ = g_main_loop_new(nullptr, FALSE);
+  if (!loop_) {
+    LOG(ERROR) << "Failed to create main loop";
+    return false;
+  }
+
+  RenewTimeout(5000);
+
+  return true;
+}
+
+bool PkgRecoveryService::RunBackend(uid_t uid, const char* type,
+    const char* file) {
+  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());
+  int status = backend.Wait();
+  if (WIFSIGNALED(status) || WEXITSTATUS(status))
+    return false;
+  return true;
+}
+
+void PkgRecoveryService::Run() {
+  g_main_loop_run(loop_);
+}
+
+void PkgRecoveryService::Finish() {
+  if (owner_id_ > 0)
+    g_bus_unown_name(owner_id_);
+  if (node_info_)
+    g_dbus_node_info_unref(node_info_);
+  if (loop_)
+    g_main_loop_unref(loop_);
+}
+
+void PkgRecoveryService::RenewTimeout(int ms) {
+  if (sid_)
+    g_source_remove(sid_);
+  sid_ = g_timeout_add(ms,
+      [](gpointer user_data) {
+        g_main_loop_quit(
+            reinterpret_cast<PkgRecoveryService*>(user_data)->loop_);
+        return FALSE;
+      },
+      this);
+}
+
+void PkgRecoveryService::HandleMethodCall(GDBusConnection* connection,
+    const gchar* sender, const gchar* object_path, const gchar* interface_name,
+    const gchar* method_name, GVariant* parameters,
+    GDBusMethodInvocation* invocation, gpointer user_data) {
+  UNUSED(connection);
+  UNUSED(sender);
+  UNUSED(object_path);
+  UNUSED(interface_name);
+  UNUSED(user_data);
+  bool r = false;
+
+  LOG(INFO) << "Incomming method call: " << method_name;
+  if (g_strcmp0(method_name, "RecoverPkg") == 0) {
+    uid_t uid;
+    char* type;
+    char* file;
+    g_variant_get(parameters, "(u&s&s)", &uid, &type, &file);
+    LOG(DEBUG) << "Recover pkg. uid: " << uid << ", type: " << type
+               << ", file: " << file;
+    r = RunBackend(uid, type, file);
+  } else {
+    LOG(ERROR) << "Unknown method call: " << method_name;
+  }
+  g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", r));
+
+  RenewTimeout(5000);
+}
+
+void PkgRecoveryService::OnBusAcquired(
+    GDBusConnection* connection, const gchar* name, gpointer user_data) {
+  UNUSED(name);
+  UNUSED(user_data);
+  GError* err = nullptr;
+  GDBusInterfaceVTable vtable = {
+    [](GDBusConnection* connection, const gchar* sender,
+        const gchar* object_path, const gchar* interface_name,
+        const gchar* method_name, GVariant* parameters,
+        GDBusMethodInvocation* invocation, gpointer user_data) {
+      reinterpret_cast<PkgRecoveryService*>(user_data)->HandleMethodCall(
+          connection, sender, object_path, interface_name, method_name,
+          parameters, invocation, user_data);
+    },
+    nullptr, nullptr, {0, }
+  };
+
+  guint reg_id = g_dbus_connection_register_object(connection, kDBusObjectPath,
+      node_info_->interfaces[0], &vtable, this, nullptr, &err);
+  if (reg_id == 0) {
+    LOG(ERROR) << "Register failed";
+    if (err) {
+      LOG(ERROR) << "Error message: " << err->message;
+      g_error_free(err);
+    }
+  } else {
+    LOG(INFO) << "DBus service registered";
+  }
+}
+
+}  // namespace
+
+int main() {
+  PkgRecoveryService service;
+  if (!service.Init()) {
+    LOG(ERROR) << "Failed to initialize service";
+    return -1;
+  }
+  service.Run();
+  return 0;
+}
diff --git a/src/pkg_recovery/pkg_recovery_helper.cc b/src/pkg_recovery/pkg_recovery_helper.cc
new file mode 100644 (file)
index 0000000..29598df
--- /dev/null
@@ -0,0 +1,109 @@
+// Copyright (c) 2017 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 <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/regex.hpp>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <tzplatform_config.h>
+#include <manifest_parser/utils/logging.h>
+#include <common/request.h>
+#include <common/utils/user_util.h>
+
+#include <string>
+#include <iostream>
+
+namespace bf = boost::filesystem;
+namespace ci = common_installer;
+
+namespace {
+
+const char kRecoveryFilePattern[] = "^(.*)-recovery-(.*)";
+const char kRecoveryMethodName[] = "RecoverPkg";
+const char kDBusServiceName[] = "org.tizen.pkg_recovery";
+const char kDBusObjectPath[] = "/org/tizen/pkg_recovery";
+const char kDBusInterfaceName[] = "org.tizen.pkg_recovery";
+const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
+
+bool RequestRecoveryService(uid_t uid, const char* type, const char* file) {
+  GError* err = nullptr;
+  GDBusConnection* con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &err);
+  if (!con || err) {
+    LOG(WARNING) << "Failed to get dbus connection: " << err->message;
+    g_error_free(err);
+    return false;
+  }
+  GDBusProxy* proxy = g_dbus_proxy_new_sync(con, G_DBUS_PROXY_FLAGS_NONE,
+      nullptr, kDBusServiceName, kDBusObjectPath, kDBusInterfaceName, nullptr,
+      &err);
+  if (!proxy) {
+    std::string err_msg;
+    if (err) {
+      err_msg = std::string(err->message);
+      g_error_free(err);
+    }
+    LOG(ERROR) << "Failed to get dbus proxy: " << err_msg;
+    g_object_unref(con);
+    return false;
+  }
+  GVariant* r = g_dbus_proxy_call_sync(proxy, kRecoveryMethodName,
+      g_variant_new("(uss)", uid, type, file), G_DBUS_CALL_FLAGS_NONE, -1,
+      nullptr, &err);
+  if (!r) {
+    std::string err_msg;
+    if (err) {
+      err_msg = std::string(err->message);
+      g_error_free(err);
+    }
+    LOG(ERROR) << "Failed to request: " << err_msg;
+    g_object_unref(proxy);
+    g_object_unref(con);
+    return false;
+  }
+  bool result;
+  g_variant_get(r, "(b)", &result);
+
+  g_variant_unref(r);
+  g_object_unref(proxy);
+  g_object_unref(con);
+
+  return result;
+}
+
+bool DoRecoveryProcess(uid_t uid) {
+  bf::path recovery_dir = ci::GetRootAppPath(false, uid);
+  for (bf::directory_iterator iter(recovery_dir);
+      iter != bf::directory_iterator();
+      ++iter) {
+    if (bf::is_directory(iter->path()))
+      continue;
+    std::string file = iter->path().filename().string();
+    boost::regex recovery_regex(kRecoveryFilePattern);
+    boost::smatch match;
+    if (boost::regex_match(file, match, recovery_regex)) {
+      LOG(INFO) << "Found recovery file: " << file;
+      std::string type(match[1]);
+      if (!RequestRecoveryService(uid, type.c_str(), iter->path().c_str()))
+        LOG(ERROR) << "Failed to recover installation: " << file;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+int main() {
+  uid_t uid = getuid();
+  DoRecoveryProcess(uid);
+  if (ci::IsAdminUser(uid))
+    DoRecoveryProcess(tzplatform_getuid(TZ_SYS_GLOBALAPP_USER));
+  return 0;
+}