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\"")
%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
%{_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
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(pkg_initdb)
+ADD_SUBDIRECTORY(pkg_recovery)
ADD_SUBDIRECTORY(pkgdir_tool)
ADD_SUBDIRECTORY(unit_tests)
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;
--- /dev/null
+# 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})
--- /dev/null
+<?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>
--- /dev/null
+[D-BUS Service]
+Name=org.tizen.pkg_recovery
+Exec=/bin/false
+SystemdService=package-recovery.service
--- /dev/null
+[Unit]
+Description=Installation Recovery Helper Service
+
+[Service]
+ExecStart=/usr/bin/pkg_recovery_helper
--- /dev/null
+[Unit]
+Description=Installation Recovery Service
+Requires=dbus.service
+
+[Service]
+User=app_fw
+Group=app_fw
+ExecStart=/usr/bin/pkg_recovery
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
+}