Add a package manager parser plugin 17/301417/6
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Tue, 14 Nov 2023 14:04:12 +0000 (15:04 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Fri, 1 Dec 2023 12:29:38 +0000 (13:29 +0100)
This is a plugin library of package manager to install ISU Packages in
the RPK format.

The RPK package should contain:
 * the content of the ISU zip archive and,
 * tizen-manifest.xml,
 * correctly generated signature files.

The tizen-manifest.xml file should contain a description of the ISU
package, for example:

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <manifest xmlns="http://tizen.org/ns/packages" api-version="1.0.0"
     package="org.tizen.isu.example_service"
     res-type="tizen.sample.resource" res-version="1.5.0" version="1.0.0">
      <isu>
  <name>example_service</name>
  <version>1.0.0</version>
      </isu>
      <allowed-package id="org.tizen.*"/>
    </manifest>

Change-Id: I3978b3e507f368ce87e47932f04b26dfaddf9a73

CMakeLists.txt
packaging/isu.spec
src/isud/isud.c
src/libisu/libisu-internal.c
src/libisu/libisu-internal.h
src/plugin_parser/CMakeLists.txt [new file with mode: 0644]
src/plugin_parser/isu-parser.c [new file with mode: 0644]
src/plugin_parser/isu-parser.info [new file with mode: 0644]

index 0e8a791..c1e299c 100644 (file)
@@ -3,3 +3,5 @@ PROJECT(isu C)
 SET(PREFIX ${CMAKE_INSTALL_PREFIX})
 ADD_SUBDIRECTORY(src/libisu)
 ADD_SUBDIRECTORY(src/isud)
+ADD_SUBDIRECTORY(src/pkg_manager)
+ADD_SUBDIRECTORY(src/plugin_parser)
index 475fb29..8aa6d30 100644 (file)
@@ -12,6 +12,8 @@ BuildRequires:  pkgconfig(dlog)
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(mount)
 BuildRequires:  pkgconfig(capi-system-info)
+BuildRequires:  pkgconfig(libxml-2.0)
+BuildRequires:  pkgconfig(pkgmgr-info)
 
 %description
 Individual Service Upgrade is Tizen mechanism used to allow extending
@@ -31,6 +33,10 @@ Provies ISU API library
 Summary:        Package provides headers needed to develop programs using ISU API
 %description -n libisu-devel
 
+%package -n     isu-plugin-parser
+Summary:        Package provides package manager parser plugin to install ISU packages.
+%description -n isu-plugin-parser
+
 %prep
 %setup -q
 
@@ -88,3 +94,10 @@ install -m750 src/pkg_manager/isu %{buildroot}/%{_bindir}/isu.sh
 %{_includedir}/libisu.h
 %{_includedir}/libisu-internal.h
 %{_libdir}/pkgconfig/*.pc
+
+
+%files -n isu-plugin-parser
+%manifest isu.manifest
+%license LICENSE.MIT
+/etc/package-manager/parserlib/libisu-parser.so*
+/usr/share/parser-plugins/isu-parser.info
index 5a69097..787c94f 100644 (file)
 
 #define TIMEOUT_INTERVAL_SEC 30
 
+enum Action {
+    Install,
+    InstallDir,
+    Uninstall,
+};
+
 static GMainLoop *loop;
 static GMutex timeout_mutex;
 static guint timeout_id;
@@ -40,6 +46,10 @@ static const gchar introspection_xml[] =
 "   <arg type='s' name='path' direction='in'/>"
 "   <arg type='i' name='result' direction='out'/>"
 "  </method>"
+"  <method name='install_dir'>"
+"   <arg type='s' name='path' direction='in'/>"
+"   <arg type='i' name='result' direction='out'/>"
+"  </method>"
 "  <method name='uninstall'>"
 "   <arg type='s' name='name' direction='in'/>"
 "   <arg type='i' name='result' direction='out'/>"
@@ -80,20 +90,40 @@ static void remove_timeout(void)
     SLOGD("Remove loop timeout");
 }
 
-static void call_handler(GDBusMethodInvocation *invocation, bool install)
+static char* action2str(enum Action action) {
+    switch (action) {
+        case Install:
+            return "install";
+        case InstallDir:
+            return "install dir";
+        case Uninstall:
+            return "uninstall";
+    }
+    return "unknown action";
+}
+
+static void call_handler(GDBusMethodInvocation *invocation, enum Action action)
 {
     GDBusMessage *incoming_message = g_dbus_method_invocation_get_message(invocation);
     GVariant *body = g_dbus_message_get_body(incoming_message);
     gchar *data;
     g_variant_get(body, "(s)", &data);
     int res;
-    if (install) {
-        res = isu_install_internal(data);
-    } else {
-        res = isu_uninstall_internal(data);
+    switch (action) {
+        case Install:
+            res = isu_install_internal(data);
+            break;
+        case InstallDir:
+            res = isu_install_dir_internal(data);
+            break;
+        case Uninstall:
+            res = isu_uninstall_internal(data);
+            break;
+        default:
+            res = -1;
     }
     SLOGD("ISU Package \"%s\" %s %s (%d)", data,
-                                           install ? "installation" : "uninstallation",
+                                           action2str(action),
                                            res == ISU_RES_OK ? "success" : "error",
                                            res);
     g_dbus_method_invocation_return_value(invocation,
@@ -102,12 +132,17 @@ static void call_handler(GDBusMethodInvocation *invocation, bool install)
 
 static void install_handler(GDBusMethodInvocation *invocation)
 {
-    call_handler(invocation, true);
+    call_handler(invocation, Install);
+}
+
+static void install_dir_handler(GDBusMethodInvocation *invocation)
+{
+    call_handler(invocation, InstallDir);
 }
 
 static void uninstall_handler(GDBusMethodInvocation *invocation)
 {
-    call_handler(invocation, false);
+    call_handler(invocation, Uninstall);
 }
 
 static void method_call_handler(GDBusConnection *conn,
@@ -122,10 +157,10 @@ static void method_call_handler(GDBusConnection *conn,
     remove_timeout();
 
     if (g_strcmp0(method_name, "install") == 0) {
-        SLOGD("Install");
         install_handler(invocation);
+    } else if (g_strcmp0(method_name, "install_dir") == 0) {
+        install_dir_handler(invocation);
     } else if (g_strcmp0(method_name, "uninstall") == 0) {
-        SLOGD("Uninstall");
         uninstall_handler(invocation);
     } else {
         SLOGE("Unsupported method: %s", method_name);
index d473736..a4304c4 100644 (file)
@@ -17,6 +17,7 @@
 #include <assert.h>
 #include <archive_entry.h>
 #include <archive.h>
+#include <errno.h>
 #include <dlog/dlog.h>
 #include <limits.h>
 #include <stdlib.h>
@@ -24,6 +25,8 @@
 #include <system_info.h>
 #include <libmount.h>
 #include <gio/gio.h>
+#include <fcntl.h>
+#include <sys/sendfile.h>
 #include "libisu-internal.h"
 
 // Arbitrarily determined block size
 #define CFG_NAME "name"
 #define CFG_VERSION "version"
 #define CFG_SYSTEM_SERVICE "system_service"
+#define DBUS_METHOD_INSTALL_DIR "install_dir"
+#define AUTHOR_SIGNATURE_FILE "author-signature.xml"
+#define SIGNATURE_FILE "signature1.xml"
+#define TIZEN_MANIFEST_FILE "tizen-manifest.xml"
+
 
 bool is_isu_feature_supported()
 {
@@ -46,6 +54,76 @@ bool is_isu_feature_supported()
     return feature_res;
 }
 
+static int copy_content(const char *src, const char *dst, const char **exclude)
+{
+    int result = -1;
+    DIR *dir;
+    struct dirent *entry;
+    dir = opendir(src);
+    if (dir == NULL) {
+        SLOGE("Cannot open directory %s: (%d) %m", src, errno);
+        return -1;
+    }
+    while ((entry = readdir(dir)) != NULL) {
+        bool ignore = false;
+
+        if (exclude != NULL) {
+            // I assume that the exclude[] will contain few elements.
+            for(size_t i = 0; exclude[i] != NULL; i++) {
+                if (strcmp(entry->d_name, exclude[i]) == 0) {
+                    ignore = true;
+                    break;
+                }
+            }
+        }
+
+        if (ignore ||
+            strncmp(entry->d_name, ".", 2) == 0 ||
+            strncmp(entry->d_name, "..", 3) == 0) {
+            continue;
+        }
+
+        char newsrc[PATH_MAX];
+        char newdst[PATH_MAX];
+        snprintf(newsrc, sizeof(newsrc), "%s/%s", src, entry->d_name);
+        snprintf(newdst, sizeof(newdst),"%s/%s", dst, entry->d_name);
+
+        int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+        if (entry->d_type == DT_DIR) {
+            if (mkdir(newdst, mode | S_IXUSR | S_IXOTH) == -1 && errno != EEXIST) {
+                SLOGE("Make directory %s error: (%d) %m", newdst, errno);
+                goto exit;
+            }
+            if (copy_content(newsrc, newdst, exclude) != 0) {
+                goto exit;
+            }
+        } else if (entry->d_type == DT_REG) {
+            int src_file = open(newsrc, O_RDONLY);
+            int dst_file = open(newdst, O_CREAT | O_WRONLY, mode);
+            if (src_file == -1 || dst_file == -1) {
+                if (src_file == -1)
+                    SLOGE("Cannot open file %s: (%d) %m", newsrc, errno);
+                if (dst_file == -1)
+                    SLOGE("Cannot open file %s: (%d) %m", newdst, errno);
+                goto exit;
+            }
+            struct stat fileinfo = {0};
+            if (fstat(src_file, &fileinfo) == -1) {
+                SLOGE("Get file %s status error: (%d) %m", newsrc, errno);
+            }
+
+            if (sendfile(dst_file, src_file, NULL, fileinfo.st_size) == -1) {
+                SLOGE("Copy file content (%s -> %s) error: (%d) %m", newsrc, newdst, errno);
+                goto exit;
+            }
+        }
+    }
+    result = 0;
+exit:
+    closedir(dir);
+    return result;
+}
+
 isu_result isu_dbus_call(const char *method, const char *parameter)
 {
     GError *error = NULL;
@@ -329,6 +407,34 @@ struct _isu_pkg_info* get_pkg_info(const char *isu_cfg_path)
     return pkg_info;
 }
 
+isu_result isu_install_dir_internal(const char *path)
+{
+    char isucfg_path[PATH_MAX];
+    snprintf(isucfg_path, sizeof(isucfg_path), "%s/%s", path, ISU_CFG);
+    struct _isu_pkg_info *pkg_info = get_pkg_info(isucfg_path);
+    if (pkg_info == NULL) {
+        return ISU_RES_ERR_INTERNAL;
+    }
+    char dst_path[PATH_MAX];
+    snprintf(dst_path, sizeof(dst_path), "%s/%s", ISU_PKG_PATH, pkg_info->name);
+    isu_pkg_info_free(pkg_info);
+
+    const char *exclude[] = {AUTHOR_SIGNATURE_FILE,
+                             SIGNATURE_FILE,
+                             TIZEN_MANIFEST_FILE,
+                             NULL};
+
+    if (mkdir(dst_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
+        errno != EEXIST) {
+        SLOGE("Create directory %s error: (%d) %m", dst_path, errno);
+        return ISU_RES_ERR_INTERNAL;
+    }
+    if (copy_content(path, dst_path, exclude) != 0) {
+        return ISU_RES_ERR_INTERNAL;
+    }
+    return ISU_RES_OK;
+}
+
 isu_result isu_install_internal(const char *path)
 {
     RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
@@ -499,3 +605,13 @@ normal_file:
     return ISU_FILE_RES_IS_NORMAL_FILE;
 }
 
+isu_result isu_install_dir(const char *path)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+
+    if (path == NULL)
+        return ISU_RES_ERR_ARGUMENT;
+
+    return isu_dbus_call(DBUS_METHOD_INSTALL_DIR, path);
+}
+
index 8b15994..ed72a15 100644 (file)
@@ -63,7 +63,10 @@ struct _isu_pkg_info {
     char **service_files;
 };
 
+isu_result isu_install_dir(const char *path);
+isu_result isu_install_dir_internal(const char *path);
 isu_result isu_install_internal(const char *path);
 isu_result isu_uninstall_internal(const char *name);
 isu_file_res is_isu_file(const char *path, pid_t pid, char **source_path);
 struct _isu_pkg_info* get_pkg_info(const char *isu_cfg_path);
+isu_result isu_dbus_call(const char *method, const char *parameter);
diff --git a/src/plugin_parser/CMakeLists.txt b/src/plugin_parser/CMakeLists.txt
new file mode 100644 (file)
index 0000000..899ba09
--- /dev/null
@@ -0,0 +1,20 @@
+PROJECT(isu-parser C)
+FIND_PACKAGE(PkgConfig)
+INCLUDE(GNUInstallDirs)
+
+pkg_check_modules(deps REQUIRED
+                  dlog
+                  capi-system-info
+                  libxml-2.0
+                  )
+
+ADD_LIBRARY(isu-parser SHARED isu-parser.c)
+SET_TARGET_PROPERTIES(isu-parser PROPERTIES VERSION ${LIBISU_VERSION})
+SET_TARGET_PROPERTIES(isu-parser PROPERTIES SOVERSION 1)
+TARGET_COMPILE_OPTIONS(isu-parser PUBLIC -fPIC)
+TARGET_INCLUDE_DIRECTORIES(isu-parser PRIVATE . ${deps_INCLUDE_DIRS} ../libisu/)
+TARGET_LINK_LIBRARIES(isu-parser PRIVATE isu ${deps_LDFLAGS})
+
+INSTALL(TARGETS isu-parser LIBRARY DESTINATION /etc/package-manager/parserlib/)
+INSTALL(FILES isu-parser.info DESTINATION /usr/share/parser-plugins/)
+
diff --git a/src/plugin_parser/isu-parser.c b/src/plugin_parser/isu-parser.c
new file mode 100644 (file)
index 0000000..f2d1a1f
--- /dev/null
@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <dlog.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <pkgmgr-info.h>
+#include "libisu.h"
+#include "libisu-internal.h"
+
+#ifndef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+#endif
+
+#ifdef LOG_TAG
+    #undef LOG_TAG
+#endif
+#define LOG_TAG "ISU_PARSER"
+
+
+int install_package(const char* packageId) {
+       pkgmgrinfo_pkginfo_h handle;
+       int result = -1;
+       int res = pkgmgrinfo_pkginfo_get_pkginfo(packageId, &handle);
+       if (res != PMINFO_R_OK) {
+               SLOGE("[%d] Cannot get pkginfo for %s", res, packageId);
+               return -1;
+       }
+
+       char *path;
+       res = pkgmgrinfo_pkginfo_get_root_path(handle, &path);
+       if (res != PMINFO_R_OK) {
+               SLOGE("[%d] Cannot get root path for %s", res, packageId);
+               goto exit;
+       }
+
+       res = isu_install_dir(path);
+       if (res == 0) {
+               SLOGE("ISU package (%s) install success", packageId);
+       } else {
+               SLOGE("[%d] ISU package (%s) install error", res, packageId);
+               goto exit;
+       }
+       result = 0;
+
+exit:
+       pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
+       return result;
+}
+
+int uninstall_package(const char* packageId) {
+       pkgmgrinfo_pkginfo_h handle;
+       int result = -1;
+       int res = pkgmgrinfo_pkginfo_get_pkginfo(packageId, &handle);
+       if (res != PMINFO_R_OK) {
+               SLOGE("[%d] Cannot get pkginfo for %s", res, packageId);
+               return -1;
+       }
+
+       char *path;
+       struct _isu_pkg_info *pkg_info = NULL;
+       res = pkgmgrinfo_pkginfo_get_root_path(handle, &path);
+       if (res != PMINFO_R_OK) {
+               SLOGE("[%d] Cannot get root path for %s", res, packageId);
+               goto exit;
+       }
+
+       char isu_cfg_path[PATH_MAX];
+       snprintf(isu_cfg_path, sizeof(isu_cfg_path), "%s/%s", path, ISU_CFG);
+       pkg_info = get_pkg_info(isu_cfg_path);
+       if (pkg_info == NULL) {
+               SLOGE("Get package info error");
+               goto exit;
+       }
+       res = isu_uninstall(pkg_info->name);
+
+       if (res == 0) {
+               SLOGD("ISU Package (%s) uninstall success", packageId);
+       } else {
+               SLOGE("[%d] ISU package (%s) uninstall error", res, packageId);
+               goto exit;
+       }
+       result = 0;
+exit:
+       if (pkg_info != NULL)
+               isu_pkg_info_free(pkg_info);
+       pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
+
+       return result;
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_PRE_INSTALL(const char* packageId)
+{
+       return 0;
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_INSTALL(xmlDocPtr docPtr, const char* packageId)
+{
+       SLOGD("Install ISU Package: %s", packageId);
+       return install_package(packageId);
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_POST_INSTALL(const char* packageId)
+{
+       return 0;
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_UNINSTALL(xmlDocPtr docPtr, const char* packageId)
+{
+       return uninstall_package(packageId);
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_PRE_UNINSTALL(const char* packageId)
+{
+       return 0;
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_POST_UNINSTALL(const char* packageId)
+{
+       return 0;
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_PRE_UPGRADE(const char* packageId)
+{
+       return 0;
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_UPGRADE(xmlDocPtr docPtr, const char* packageId)
+{
+       SLOGD("Upgrade ISU Pacakge: %s", packageId);
+       return install_package(packageId);
+}
+
+EXPORT_API
+int PKGMGR_PARSER_PLUGIN_POST_UPGRADE(const char* packageId)
+{
+       return 0;
+}
diff --git a/src/plugin_parser/isu-parser.info b/src/plugin_parser/isu-parser.info
new file mode 100644 (file)
index 0000000..bc3ee99
--- /dev/null
@@ -0,0 +1 @@
+type="tag";name="isu";path="/etc/package-manager/parserlib/libisu-parser.so"