Add ISU API 90/299090/23
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Tue, 3 Oct 2023 11:23:21 +0000 (13:23 +0200)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Fri, 1 Dec 2023 12:29:38 +0000 (13:29 +0100)
Change-Id: I935c5e76b7cf061d3f8ef451ca527b0be96e0121

14 files changed:
CMakeLists.txt [new file with mode: 0644]
config/isu.conf
packaging/isu.spec
src/isud/CMakeLists.txt [new file with mode: 0644]
src/isud/isud.c [new file with mode: 0644]
src/isud/isud.conf [new file with mode: 0644]
src/isud/isud.service [new file with mode: 0644]
src/isud/org.tizen.system.isu.service [new file with mode: 0644]
src/libisu/CMakeLists.txt [new file with mode: 0644]
src/libisu/libisu-internal.c [new file with mode: 0644]
src/libisu/libisu-internal.h [new file with mode: 0644]
src/libisu/libisu.c [new file with mode: 0644]
src/libisu/libisu.h [new file with mode: 0644]
src/libisu/libisu.pc.in [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0e8a791
--- /dev/null
@@ -0,0 +1,5 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 3.6)
+PROJECT(isu C)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+ADD_SUBDIRECTORY(src/libisu)
+ADD_SUBDIRECTORY(src/isud)
index ad26c18..177847b 100644 (file)
@@ -1,4 +1,4 @@
-d       /opt/isu       0755    root    root    -
+d       /opt/isu       0755    system    system        -
 t       /opt/isu       -       -       -       -       security.SMACK64="_"
-d       /run/isu       0755    root    root    -
+d       /run/isu       0755    system    system        -
 t       /run/isu       -       -       -       -       security.SMACK64="_"
index 083f27e..361bc6a 100644 (file)
@@ -1,9 +1,17 @@
+%define libisu_version 0.0.1
 Summary: Individual Service Upgrade support
 Name:    isu
 Version: 8.0.16
 Release: 1
 Source0: %{name}-%{version}.tar.gz
 License: MIT
+BuildRequires:  cmake
+BuildRequires:  pkgconfig
+BuildRequires:  pkgconfig(libarchive)
+BuildRequires:  pkgconfig(dlog)
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(mount)
+BuildRequires:  pkgconfig(capi-system-info)
 
 %description
 Individual Service Upgrade is Tizen mechanism used to allow extending
@@ -14,13 +22,27 @@ ISU uses systemd.generator(7) mechanism to install systemd services
 from extension packages and modified bubblewrap to run service in
 partially sandboxed environment.
 
+%package -n     libisu
+Summary:        ISU API library
+%description -n libisu
+Provies ISU API library
+
+%package -n     libisu-devel
+Summary:        Package provides headers needed to develop programs using ISU API
+%description -n libisu-devel
+
 %prep
 %setup -q
 
 %build
 cp packaging/isu.manifest .
+%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DLIBISU_VERSION=%{libisu_version}
+make %{?_smp_mflags}
 
 %install
+rm -rf %{buildroot}
+%make_install
+
 mkdir -p %{buildroot}/usr/libexec
 mkdir -p %{buildroot}/usr/lib/systemd/system-generators
 mkdir -p %{buildroot}/usr/lib/systemd/user-generators
@@ -47,3 +69,21 @@ install -m750 src/pkg_manager/isu %{buildroot}/%{_bindir}/isu
 /etc/isu/user-service-common.inc
 /etc/isu/upgrade.cfg
 %{_bindir}/isu
+
+%files -n libisu
+%manifest isu.manifest
+%license LICENSE.MIT
+%{_libdir}/libisu.so.*
+%{_bindir}/isud
+%attr(-,root,root) /etc/dbus-1/system.d/isud.conf
+%attr(-,root,root) %{_unitdir}/isud.service
+%attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.isu.service
+
+
+%files -n libisu-devel
+%manifest isu.manifest
+%license LICENSE.MIT
+%{_libdir}/libisu.so
+%{_includedir}/libisu.h
+%{_includedir}/libisu-internal.h
+%{_libdir}/pkgconfig/*.pc
diff --git a/src/isud/CMakeLists.txt b/src/isud/CMakeLists.txt
new file mode 100644 (file)
index 0000000..51904a3
--- /dev/null
@@ -0,0 +1,22 @@
+FIND_PACKAGE(PkgConfig)
+INCLUDE(GNUInstallDirs)
+
+pkg_check_modules(deps REQUIRED
+                      dlog
+                      gio-2.0
+                      gio-unix-2.0
+                      capi-system-info
+                  )
+
+
+ADD_EXECUTABLE(isud isud.c)
+TARGET_COMPILE_OPTIONS(isud PRIVATE ${deps_CFLAGS_OTHER} -fPIC)
+TARGET_INCLUDE_DIRECTORIES(isud PRIVATE ${deps_INCLUDE_DIRS} ../libisu/)
+TARGET_LINK_LIBRARIES(isud PRIVATE isu ${deps_LDFLAGS} -pie -larchive)
+
+
+INSTALL(TARGETS isud DESTINATION bin)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/isud.conf DESTINATION /etc/dbus-1/system.d)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/isud.service DESTINATION /usr/lib/systemd/system)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.system.isu.service DESTINATION /usr/share/dbus-1/system-services)
+
diff --git a/src/isud/isud.c b/src/isud/isud.c
new file mode 100644 (file)
index 0000000..5a69097
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 <assert.h>
+#include <gio/gio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlog/dlog.h>
+
+#include "libisu-internal.h"
+
+#ifdef LOG_TAG
+    #undef LOG_TAG
+#endif
+#define LOG_TAG "ISUSERVICE"
+
+#define TIMEOUT_INTERVAL_SEC 30
+
+static GMainLoop *loop;
+static GMutex timeout_mutex;
+static guint timeout_id;
+static GDBusNodeInfo *introspection_data;
+static const gchar introspection_xml[] =
+"<node>"
+" <interface name='org.tizen.system.isu'>"
+"  <method name='install'>"
+"   <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'/>"
+"  </method>"
+" </interface>"
+"</node>";
+
+static int timeout_cb(gpointer data)
+{
+    SLOGI("Time out!");
+    g_main_loop_quit((GMainLoop *)data);
+
+    return 0;
+}
+
+static void add_timeout(void)
+{
+    g_mutex_lock(&timeout_mutex);
+
+    if (timeout_id)
+        g_source_remove(timeout_id);
+    timeout_id = g_timeout_add_seconds(TIMEOUT_INTERVAL_SEC, timeout_cb, loop);
+
+    g_mutex_unlock(&timeout_mutex);
+    SLOGD("Add loop timeout (%d)", TIMEOUT_INTERVAL_SEC);
+}
+
+static void remove_timeout(void)
+{
+    g_mutex_lock(&timeout_mutex);
+
+    if (timeout_id) {
+        g_source_remove(timeout_id);
+        timeout_id = 0;
+    }
+
+    g_mutex_unlock(&timeout_mutex);
+    SLOGD("Remove loop timeout");
+}
+
+static void call_handler(GDBusMethodInvocation *invocation, bool install)
+{
+    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);
+    }
+    SLOGD("ISU Package \"%s\" %s %s (%d)", data,
+                                           install ? "installation" : "uninstallation",
+                                           res == ISU_RES_OK ? "success" : "error",
+                                           res);
+    g_dbus_method_invocation_return_value(invocation,
+                                          g_variant_new("(i)", res));
+}
+
+static void install_handler(GDBusMethodInvocation *invocation)
+{
+    call_handler(invocation, true);
+}
+
+static void uninstall_handler(GDBusMethodInvocation *invocation)
+{
+    call_handler(invocation, false);
+}
+
+static void method_call_handler(GDBusConnection *conn,
+                            const gchar *sender,
+                            const gchar *object_path,
+                            const gchar *iface_name,
+                            const gchar *method_name,
+                            GVariant *parameters,
+                            GDBusMethodInvocation *invocation,
+                            gpointer user_data)
+{
+    remove_timeout();
+
+    if (g_strcmp0(method_name, "install") == 0) {
+        SLOGD("Install");
+        install_handler(invocation);
+    } else if (g_strcmp0(method_name, "uninstall") == 0) {
+        SLOGD("Uninstall");
+        uninstall_handler(invocation);
+    } else {
+        SLOGE("Unsupported method: %s", method_name);
+    }
+
+    add_timeout();
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+    method_call_handler,
+    NULL,
+    NULL
+};
+
+static void on_bus_acquired(GDBusConnection *conn,
+                        const gchar *name,
+                        gpointer user_data)
+{
+    const char *object_path;
+    GDBusInterfaceInfo *interface_info;
+    guint registration_id;
+    GError *error = NULL;
+
+    if (strcmp(name, ISUSERVICE_BUS_NAME) == 0) {
+        object_path = ISUSERVICE_OBJECT_PATH;
+        interface_info = introspection_data->interfaces[0];
+    } else {
+        SLOGE("on_bus_acquired: unknown name: %s", name);
+        return;
+    }
+
+    registration_id = g_dbus_connection_register_object(conn,
+                                                        object_path,
+                                                        interface_info,
+                                                        &interface_vtable,
+                                                        NULL,
+                                                        NULL,
+                                                        &error);
+    if (registration_id == 0 || error) {
+        SLOGD("Failed to g_dbus_connection_register_object for %s: %s",
+              name,
+              error ? error->message : "");
+        if (error)
+            g_error_free(error);
+    }
+}
+
+static void on_name_acquired(GDBusConnection *conn,
+                         const gchar *name,
+                         gpointer user_data)
+{
+    SLOGD("Acquired the name %s on the system bus", name);
+}
+
+static void on_name_lost(GDBusConnection *conn,
+                     const gchar *name,
+                     gpointer user_data)
+{
+    SLOGD("Lost the name %s on the system bus", name);
+}
+
+static bool dbus_init(void)
+{
+    GError *error = NULL;
+
+    introspection_data =
+        g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+    if (introspection_data == NULL) {
+        SLOGE("Failed to init g_dbus_info_new_for_xml");
+        return false;
+    }
+
+    GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+    if (!conn || error) {
+        SLOGE("Failed to get dbus: %s", error ? error->message : "");
+        if (error)
+            g_error_free(error);
+        return false;
+    }
+
+    g_bus_own_name(G_BUS_TYPE_SYSTEM, ISUSERVICE_BUS_NAME,
+                   G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
+                   on_name_acquired, on_name_lost, NULL, NULL);
+
+    return true;
+}
+
+int main(void)
+{
+    loop = g_main_loop_new(NULL, false);
+    if (loop == NULL) {
+        SLOGE("GLib main loop create error");
+        return EXIT_FAILURE;
+    }
+
+    if (!dbus_init()) {
+        g_main_loop_unref(loop);
+        return EXIT_FAILURE;
+    }
+
+    g_mutex_init(&timeout_mutex);
+    add_timeout();
+
+    g_main_loop_run(loop);
+
+    if (introspection_data)
+        g_dbus_node_info_unref(introspection_data);
+    g_mutex_clear(&timeout_mutex);
+    g_main_loop_unref(loop);
+
+    return EXIT_SUCCESS;
+}
diff --git a/src/isud/isud.conf b/src/isud/isud.conf
new file mode 100644 (file)
index 0000000..1797911
--- /dev/null
@@ -0,0 +1,22 @@
+<!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.system.isu"/>
+               <allow send_destination="org.tizen.system.isu"
+                      send_interface="org.tizen.system.isu"/>
+       </policy>
+
+       <policy user="system">
+               <allow own="org.tizen.system.isu"/>
+       </policy>
+
+       <policy context="default">
+               <deny own="org.tizen.system.isu"/>
+               <deny send_destination="org.tizen.system.isu"/>
+
+               <check send_destination="org.tizen.system.isu"
+                      send_interface="org.tizen.system.isu"
+                      privilege="http://tizen.org/privilege/isu.admin"/>
+       </policy>
+</busconfig>
diff --git a/src/isud/isud.service b/src/isud/isud.service
new file mode 100644 (file)
index 0000000..b720b4c
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Individual Service Upgrade management daemon
+
+[Service]
+Type=dbus
+User=system
+Group=system
+BusName=org.tizen.system.isu
+ExecStart=/usr/bin/isud
+SmackProcessLabel=System
+KillMode=mixed
diff --git a/src/isud/org.tizen.system.isu.service b/src/isud/org.tizen.system.isu.service
new file mode 100644 (file)
index 0000000..c9a5539
--- /dev/null
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=org.tizen.system.isu
+Exec=/bin/false
+SystemdService=isud.service
+
diff --git a/src/libisu/CMakeLists.txt b/src/libisu/CMakeLists.txt
new file mode 100644 (file)
index 0000000..509d5e5
--- /dev/null
@@ -0,0 +1,33 @@
+FIND_PACKAGE(PkgConfig)
+INCLUDE(GNUInstallDirs)
+
+pkg_check_modules(deps REQUIRED
+                      dlog
+                      gio-2.0
+                      gio-unix-2.0
+                      capi-system-info
+                      mount
+                  )
+
+
+SET(PC_VERSION ${LIBISU_VERSION})
+SET(PC_PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(PC_NAME ${PROJECT_NAME})
+SET(PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
+SET(PC_INCLUDE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
+SET(PC_DESCRIPTION "Individual Service Upgrade support library")
+SET(PC_REQUIRED "dlog glib-2.0 gio-unix-2.0 capi-system-info")
+SET(PC_LDFLAGS -lisu)
+CONFIGURE_FILE(libisu.pc.in ${CMAKE_CURRENT_SOURCE_DIR}/libisu.pc @ONLY)
+
+ADD_LIBRARY(isu SHARED libisu.c libisu-internal.c)
+SET_TARGET_PROPERTIES(isu PROPERTIES VERSION ${LIBISU_VERSION})
+SET_TARGET_PROPERTIES(isu PROPERTIES SOVERSION 1)
+SET_TARGET_PROPERTIES(isu PROPERTIES PUBLIC_HEADER libisu.h)
+TARGET_COMPILE_OPTIONS(isu PUBLIC -fPIE)
+TARGET_INCLUDE_DIRECTORIES(isu PRIVATE . ${deps_INCLUDE_DIRS})
+
+INSTALL(TARGETS isu PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libisu.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libisu-internal.h DESTINATION ${INCLUDE_INSTALL_DIR})
+
diff --git a/src/libisu/libisu-internal.c b/src/libisu/libisu-internal.c
new file mode 100644 (file)
index 0000000..d473736
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+#define _GNU_SOURCE
+#include <assert.h>
+#include <archive_entry.h>
+#include <archive.h>
+#include <dlog/dlog.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <ftw.h>
+#include <system_info.h>
+#include <libmount.h>
+#include <gio/gio.h>
+#include "libisu-internal.h"
+
+// Arbitrarily determined block size
+#define BLOCK_SIZE 10240
+#define CFG_NAME "name"
+#define CFG_VERSION "version"
+#define CFG_SYSTEM_SERVICE "system_service"
+
+bool is_isu_feature_supported()
+{
+    int ret;
+    bool feature_res;
+
+    ret = system_info_get_platform_bool(ISU_FEATURE, &feature_res);
+    if (ret != SYSTEM_INFO_ERROR_NONE) {
+        SLOGE("Failed to get feature info");
+        return false;
+    }
+
+    return feature_res;
+}
+
+isu_result isu_dbus_call(const char *method, const char *parameter)
+{
+    GError *error = NULL;
+    int res;
+    GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+    if (conn == NULL) {
+        SLOGE("isu_%s: g_bus_get_sync error: %s", method, error ? error->message : "");
+        if (error)
+            g_error_free(error);
+        return ISU_RES_ERR_INTERNAL;
+    }
+
+    GDBusMessage *msg = g_dbus_message_new_method_call(
+                            ISUSERVICE_BUS_NAME,
+                            ISUSERVICE_OBJECT_PATH,
+                            ISUSERVICE_INTERFACE_NAME,
+                            method);
+    if (msg == NULL) {
+        SLOGE("isu_%s: g_dbus_message_new_method_call error", method);
+        return ISU_RES_ERR_INTERNAL;
+    }
+
+    g_dbus_message_set_body(msg, g_variant_new("(s)", parameter));
+
+    GDBusMessage *reply = g_dbus_connection_send_message_with_reply_sync(
+                                conn,
+                                msg,
+                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                -1,
+                                NULL,
+                                NULL,
+                                &error);
+    if (reply == NULL) {
+        SLOGE("isu_%s: send message error: %s", method, error ? error->message : "" );
+        g_error_free(error);
+    }
+
+    GDBusMessageType msg_type = g_dbus_message_get_message_type(reply);
+    if (msg_type == G_DBUS_MESSAGE_TYPE_ERROR) {
+        SLOGE("isu_%s: No permission to call this method", method);
+        res = ISU_RES_ERR_NOT_PERMITTED;
+        goto exit;
+    }
+
+    GVariant *body = g_dbus_message_get_body(reply);
+    if (body == NULL) {
+        SLOGE("isu_%s: get message body error", method);
+        return ISU_RES_ERR_INTERNAL;
+    }
+    g_variant_get(body, "(i)", &res);
+exit:
+    g_object_unref(msg);
+    g_object_unref(reply);
+    g_object_unref(conn);
+    return res;
+}
+
+static void fill_system_services(char *line, struct _isu_pkg_info *pkg_info)
+{
+    char *saveptr;
+    char *token = strtok_r(line, " ", &saveptr);
+    while (token != NULL) {
+        pkg_info->service_files_len++;
+        pkg_info->service_files = realloc(pkg_info->service_files,
+                                          pkg_info->service_files_len*sizeof(char*));
+        pkg_info->service_files[pkg_info->service_files_len-1] = strdup(token);
+        token = strtok_r(NULL, " ", &saveptr);
+    }
+}
+
+static int copy_data(struct archive *ar, struct archive *aw)
+{
+    assert(ar);
+    assert(aw);
+
+    int res;
+    const void *buff;
+    size_t size;
+    la_int64_t offset;
+
+    for (;;) {
+        res = archive_read_data_block(ar, &buff, &size, &offset);
+        if (res == ARCHIVE_EOF)
+            return ARCHIVE_OK;
+        if (res == ARCHIVE_RETRY)
+            continue;
+        if (res < ARCHIVE_OK) {
+            SLOGE("Archive read data block error (%d): %s\n", res, archive_error_string(ar));
+            return res;
+        }
+        res = archive_write_data_block(aw, buff, size, offset);
+        if (res < ARCHIVE_OK) {
+            SLOGE("Archive write data block error (%d): %s\n", res, archive_error_string(aw));
+            return res;
+        }
+    }
+}
+
+static int extract(const char *arch_path, const char *dest_path, const char *file_name)
+{
+    assert(arch_path);
+    assert(dest_path);
+
+    struct archive *a;
+    struct archive *ext;
+    struct archive_entry *entry;
+    int res;
+    int result = ISU_RES_ERR_INVALID_FORMAT;
+    int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR;
+
+    a = archive_read_new();
+    archive_read_support_format_all(a);
+    archive_read_support_filter_all(a);
+    ext = archive_write_disk_new();
+    archive_write_disk_set_options(ext, flags);
+    archive_write_disk_set_standard_lookup(ext);
+
+    res = archive_read_open_filename(a, arch_path, BLOCK_SIZE);
+    if (res != 0) {
+        SLOGE("Open archive %s error (%d): %s\n", arch_path, res, archive_error_string(a));
+        goto exit;
+    }
+    for (;;) {
+        res = archive_read_next_header(a, &entry);
+        if (res == ARCHIVE_EOF) {
+            result = ISU_RES_OK;
+            break;
+        }
+        if (res < ARCHIVE_OK) {
+            SLOGE("Archive read next header error (%d): %s\n", res, archive_error_string(a));
+            break;
+        }
+
+        const char *entry_path = archive_entry_pathname(entry);
+        if (entry_path == NULL) {
+            SLOGE("Entry path is empty");
+            continue;
+        }
+        if (file_name != NULL) {
+            // In case we are extracting a particular file (file_name != NULL),
+            // skip if the current one is not the one we are looking for
+            if (strcmp(entry_path, file_name) != 0) {
+                continue;
+            }
+        }
+
+        char new_entry_path[PATH_MAX];
+        snprintf(new_entry_path, sizeof(new_entry_path), "%s/%s", dest_path, entry_path);
+        archive_entry_set_pathname(entry, new_entry_path);
+
+        res = archive_write_header(ext, entry);
+        if (res < ARCHIVE_OK) {
+            SLOGE("write error: %s\n", archive_error_string(ext));
+            result = ISU_RES_ERR_INTERNAL;
+            break;
+        } else if (archive_entry_size(entry) > 0) {
+            res = copy_data(a, ext);
+            if (res < ARCHIVE_OK) {
+                SLOGE("copy data error: %s\n", archive_error_string(ext));
+                result = ISU_RES_ERR_INTERNAL;
+                break;
+            }
+        }
+
+        res = archive_write_finish_entry(ext);
+        if (res < ARCHIVE_OK) {
+            SLOGE("write finish error: %s\n", archive_error_string(ext));
+            result = ISU_RES_ERR_INTERNAL;
+            break;
+        }
+    }
+
+exit:
+    archive_read_close(a);
+    archive_read_free(a);
+    archive_write_close(ext);
+    archive_write_free(ext);
+
+    return result;
+}
+
+static bool path_exists(const char *path)
+{
+    assert(path);
+
+    struct stat st;
+    return stat(path, &st) == 0;
+}
+
+static int rm_cb(const char *path, const struct stat *sb, int type_flag, struct FTW *ftw)
+{
+    assert(path);
+    assert(sb);
+    assert(ftw);
+
+    int res = remove(path);
+    if (res != 0) {
+        SLOGE("Remove %s error (%d): %m\n", path, errno);
+    }
+    return res;
+}
+
+static int remove_dir(const char *path)
+{
+    assert(path);
+    return nftw(path, rm_cb, FTW_F | FTW_D, FTW_DEPTH | FTW_PHYS);
+}
+
+static struct _isu_pkg_info* isu_pkg_info_prepare()
+{
+    struct _isu_pkg_info *pkg_info = malloc(sizeof(struct _isu_pkg_info));
+    if (pkg_info == NULL) {
+        SLOGE("Memory allocation error (%d): %m\n", errno);
+        return NULL;
+    }
+    pkg_info->name = NULL;
+    pkg_info->version = NULL;
+    pkg_info->service_files_len = 0;
+    pkg_info->service_files = NULL;
+
+    return pkg_info;
+}
+
+struct _isu_pkg_info* get_pkg_info(const char *isu_cfg_path)
+{
+    assert(isu_cfg_path);
+
+    struct _isu_pkg_info *pkg_info = isu_pkg_info_prepare();
+    if (pkg_info == NULL) {
+        return NULL;
+    }
+
+    FILE *f = fopen(isu_cfg_path, "r");
+    if (f == NULL) {
+        SLOGE("File %s open error (%d): %m\n", isu_cfg_path, errno);
+        free(pkg_info);
+        pkg_info = NULL;
+        return NULL;
+    }
+
+    char *lineptr = NULL;
+    size_t n = 0;
+    while (getline(&lineptr, &n, f) > 0) {
+        char *line = lineptr;
+        while (line && line[0] == ' ') { line++; };
+        if (line == NULL) continue;
+        if (line[0] == '#') continue;
+
+        char *saveptr;
+        char *token = strtok_r(line, "=", &saveptr);
+        if (!token) continue;
+        char *value = strtok_r(NULL, "=", &saveptr);
+        if (!value) continue;
+        while (value[0] == ' ') { value++; };
+
+        size_t val_len = strlen(value);
+
+        if (val_len > 0 && value[val_len-1] == '\n') {
+            value[val_len-1] = '\0';
+        }
+
+        if (strncmp(token, CFG_NAME, strlen(CFG_NAME)) == 0) {
+            pkg_info->name = strdup(value);
+        } else if (strncmp(token, CFG_VERSION, strlen(CFG_VERSION)) == 0) {
+            pkg_info->version = strdup(value);
+        } else if (strncmp(token, CFG_SYSTEM_SERVICE, strlen(CFG_SYSTEM_SERVICE)) == 0) {
+            fill_system_services(value, pkg_info);
+        }
+
+    }
+    if (!pkg_info->name || !pkg_info->version) {
+        SLOGE("Cannot read content of isu.cfg");
+        isu_pkg_info_free(pkg_info);
+        pkg_info = NULL;
+    }
+
+    free(lineptr);
+    if (f != NULL) {
+        fclose(f);
+    }
+    return pkg_info;
+}
+
+isu_result isu_install_internal(const char *path)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+    assert(path);
+
+    int result = ISU_RES_ERR_INVALID_FORMAT;
+    struct _isu_pkg_info *pkg_info = NULL;
+
+    char temp_path[] = "/tmp/isu-XXXXXX";
+    if (mkdtemp(temp_path) == NULL) {
+        result = ISU_RES_ERR_INTERNAL;
+        goto exit;
+    }
+
+    if (extract(path, temp_path, ISU_CFG) != ISU_RES_OK) {
+        goto exit;
+    }
+
+    char buff[PATH_MAX];
+    snprintf(buff, sizeof(buff), "%s/%s", temp_path, ISU_CFG);
+    pkg_info = get_pkg_info(buff);
+    if (pkg_info == NULL) {
+        SLOGE("File %s is not an ISU Package\n", path);
+        goto exit;
+    }
+    if (pkg_info->name == NULL) {
+        SLOGE("Package %s does not contain the name\n", path);
+        goto exit;
+    }
+
+    snprintf(buff, sizeof(buff), "%s/%s", ISU_PKG_PATH, pkg_info->name);
+    if (path_exists(buff) && remove_dir(buff) != 0) {
+        goto exit;
+    }
+
+    if (extract(path, buff, NULL) != ISU_RES_OK) {
+        goto exit;
+    }
+
+    result = ISU_RES_OK;
+
+exit:
+    if (result == ISU_RES_OK) {
+        SLOGI("ISU Package (%s %s) install success\n", pkg_info->name, pkg_info->version);
+    } else {
+        SLOGE("ISU Package (%s) install error: %d\n", path, result);
+    }
+
+    if (path_exists(temp_path)) {
+        remove_dir(temp_path);
+    }
+
+    isu_pkg_info_free(pkg_info);
+    return result;
+}
+
+isu_result isu_uninstall_internal(const char *name)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+    assert(name);
+
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "%s/%s", ISU_PKG_PATH, name);
+    int result = remove_dir(path);
+    if (result == 0) {
+        SLOGI("ISU Package (%s) uninstall success\n", name);
+        result = ISU_RES_OK;
+    } else {
+        SLOGE("ISU Package (%s) uninstall error: %d\n", name, result);
+        result = ISU_RES_ERR_NOT_EXIST;
+    }
+    return result;
+}
+
+void cleanup_libmnt_table(struct libmnt_table **table)
+{
+    mnt_unref_table(*table);
+}
+
+void cleanup_char_ptr(char **ptr)
+{
+    free(*ptr);
+}
+
+isu_file_res is_isu_file(const char *path, pid_t pid, char **source_path)
+{
+    if (!is_isu_feature_supported())
+        return ISU_FILE_RES_NOT_SUPPORTED;
+
+    assert(path);
+    assert(source_path);
+    *source_path = NULL;
+
+    char mountinfo_path[PATH_MAX];
+    snprintf(mountinfo_path, sizeof(mountinfo_path), "/proc/%d/mountinfo", pid);
+
+    __attribute__ ((__cleanup__(cleanup_libmnt_table))) struct libmnt_table *table = mnt_new_table_from_file(mountinfo_path);
+    if (table == NULL) {
+        SLOGW("Invalid mountinfo path (%s)\n", mountinfo_path);
+        return ISU_FILE_RES_ERROR;
+    }
+
+    __attribute__ ((__cleanup__(cleanup_char_ptr))) char *fname_orig = mnt_resolve_path(path, NULL);
+
+    /*
+     * Check if the fname_orig file or it subpath is bindmounted
+     */
+    struct libmnt_fs *fs = NULL;
+    do {
+        if ((fs = mnt_table_find_target(table, fname_orig, MNT_ITER_FORWARD)) != NULL) {
+            /*
+             * Yes, fname_orig is mounted
+             */
+            break;
+        }
+        char *slash = strrchr(fname_orig, '/');
+        if (slash == NULL)
+            break;
+        /*
+         * Cut off the last part of the path
+         */
+        *slash = '\0';
+    } while (true);
+
+    if (fs == NULL) {
+        goto normal_file;
+    }
+
+    const char *source = mnt_fs_get_source(fs);
+    if (source == NULL) {
+        SLOGE("mnt_fs_get_source error\n");
+        return ISU_FILE_RES_ERROR;
+    }
+    /*
+     * Check if the fname_orig path is mounted from the loop device
+     */
+    if (strstr(source, "/dev/loop") == NULL) {
+        goto normal_file;
+    }
+
+    /*
+     * If so, get the path in the mounted image
+     */
+    char *in_image = strdup(mnt_fs_get_root(fs));
+    if (in_image == NULL) {
+        SLOGE("Memory allocation error");
+        return ISU_FILE_RES_ERROR;
+    }
+
+    fs = mnt_table_find_source(table, mnt_fs_get_source(fs), MNT_ITER_FORWARD);
+    if (fs == NULL) {
+        SLOGE("mnt_table_find_source error");
+        return ISU_FILE_RES_ERROR;
+    }
+
+    /*
+     * Get the full path of the file outside the sandbox
+     */
+    const char *img_mount_dir = mnt_fs_get_target(fs);
+    if (asprintf(source_path, "%s/%s", img_mount_dir, in_image) == -1) {
+        SLOGE("Memory allocation error");
+        return ISU_FILE_RES_ERROR;
+    }
+
+    return ISU_FILE_RES_IS_ISU_FILE;
+
+normal_file:
+    return ISU_FILE_RES_IS_NORMAL_FILE;
+}
+
diff --git a/src/libisu/libisu-internal.h b/src/libisu/libisu-internal.h
new file mode 100644 (file)
index 0000000..8b15994
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+#pragma once
+#include <stdbool.h>
+#include "libisu.h"
+
+#define ISUSERVICE_BUS_NAME          "org.tizen.system.isu"
+#define ISUSERVICE_OBJECT_PATH       "/Org/Tizen/System/Isu"
+#define ISUSERVICE_INTERFACE_NAME    ISUSERVICE_BUS_NAME
+#define ISU_FEATURE "http://tizen.org/feature/isu"
+#define ISU_PKG_PATH "/opt/isu/"
+#define ISU_RUN_PATH "/run/isu/"
+#define ISU_CFG "isu.cfg"
+
+#ifdef LOG_TAG
+    #undef LOG_TAG
+#endif
+#define LOG_TAG "LIBISU"
+
+typedef enum {
+       ISU_FILE_RES_NOT_SUPPORTED = -2,
+       ISU_FILE_RES_ERROR = -1,
+       ISU_FILE_RES_IS_NORMAL_FILE = 0,
+       ISU_FILE_RES_IS_ISU_FILE = 1,
+} isu_file_res;
+
+struct _isu_pkg_list {
+    struct _isu_pkg_list_element *head, *cur;
+};
+
+bool is_isu_feature_supported();
+#define RET_IF_FEATURE_NOT_SUPPORTED(value) \
+        { \
+            if (!is_isu_feature_supported()) { \
+                SLOGE("ISU feature is not supported"); \
+                return value; \
+            } \
+        } while(0) \
+
+
+struct _isu_pkg_list_element {
+    char *name;
+    struct _isu_pkg_list_element *next;
+};
+
+struct _isu_pkg_info {
+    char *name;
+    char *version;
+    size_t service_files_len;
+    char **service_files;
+};
+
+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);
diff --git a/src/libisu/libisu.c b/src/libisu/libisu.c
new file mode 100644 (file)
index 0000000..4270c4e
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <linux/limits.h>
+#include <limits.h>
+#include <ftw.h>
+#include <errno.h>
+#include <dirent.h>
+#include <assert.h>
+
+#include <dlog/dlog.h>
+
+#include "libisu-internal.h"
+
+
+static bool is_valid_isu_pkg(const char *path)
+{
+    assert(path);
+
+    struct stat statbuff;
+    char pathbuff[PATH_MAX+8];
+    snprintf(pathbuff, sizeof(pathbuff), "%s/%s", path, ISU_CFG);
+
+    return stat(pathbuff, &statbuff) == 0;
+}
+
+/*
+ * Public API Functions
+ */
+
+isu_pkg_list isu_list_init()
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(NULL);
+    struct _isu_pkg_list *pkg_list = malloc(sizeof(struct _isu_pkg_list));
+    if (pkg_list == NULL) {
+        SLOGE("Memory allocation error (%d): %m\n", errno);
+        return NULL;
+    }
+
+    pkg_list->head = NULL;
+    pkg_list->cur = NULL;
+    DIR *dir = opendir(ISU_PKG_PATH);
+    if (dir == NULL) {
+        SLOGE("Open dir %s error (%d): %m\n", ISU_PKG_PATH, errno);
+        return NULL;
+    }
+
+    struct _isu_pkg_list_element *prev = NULL;
+    struct dirent *entry;
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_type != DT_DIR) continue;
+
+        char pathbuff[PATH_MAX];
+        snprintf(pathbuff, sizeof(pathbuff), "%s/%s", ISU_PKG_PATH, entry->d_name);
+        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+            continue;
+
+        if (is_valid_isu_pkg(pathbuff)) {
+            struct _isu_pkg_list_element *element = malloc(sizeof(struct _isu_pkg_list_element));
+            if (element == NULL) {
+                SLOGE("Memory allocation error (%d): %m\n", errno);
+                isu_list_free(pkg_list);
+                closedir(dir);
+                return NULL;
+            }
+            element->name = strdup(entry->d_name);
+            element->next = NULL;
+            if (prev == NULL) {
+                pkg_list->head = element;
+                pkg_list->cur = element;
+            } else {
+                prev->next = element;
+            }
+            prev = element;
+        } else {
+            SLOGW("Invalid ISU package directory: %s\n", pathbuff);
+        }
+    }
+
+    closedir(dir);
+
+    return pkg_list;
+}
+
+isu_result isu_list_free(isu_pkg_list pkg_list)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+    if (pkg_list == NULL)
+        return ISU_RES_OK;
+
+    struct _isu_pkg_list *pkg_list_i = (struct _isu_pkg_list*)pkg_list;
+
+    struct _isu_pkg_list_element *element = pkg_list_i->head;
+    while (element != NULL) {
+        struct _isu_pkg_list_element *tmp = element->next;
+        free(element->name);
+        free(element);
+        element = tmp;
+    }
+
+    free(pkg_list);
+    return ISU_RES_OK;
+}
+
+isu_pkg_info isu_list_next(isu_pkg_list pkg_list)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(NULL);
+    struct _isu_pkg_list *pkg_list_i = (struct _isu_pkg_list *)pkg_list;
+
+    if (pkg_list == NULL) {
+        SLOGE("Argument is NULL");
+        return NULL;
+    }
+
+    if (pkg_list_i->cur == NULL)
+        return NULL;
+
+    char isu_cfg_path[PATH_MAX];
+    snprintf(isu_cfg_path, sizeof(isu_cfg_path), "%s/%s/%s", ISU_PKG_PATH, pkg_list_i->cur->name, ISU_CFG);
+    isu_pkg_info pkg_info = get_pkg_info(isu_cfg_path);
+
+    pkg_list_i->cur = pkg_list_i->cur->next;
+    return pkg_info;
+}
+
+isu_result isu_pkg_get_name(isu_pkg_info pkg_info, char *name, size_t len)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+
+    if (pkg_info == NULL || name == NULL)
+        return ISU_RES_ERR_ARGUMENT;
+
+    struct _isu_pkg_info* pkg_info_i = (struct _isu_pkg_info*)pkg_info;
+
+    if (len < strlen(pkg_info_i->name)) {
+        return ISU_RES_ERR_BUFF_TOO_SMALL;
+    }
+
+    if (snprintf(name, len, "%s", pkg_info_i->name) < strlen(pkg_info_i->name)) {
+        return ISU_RES_ERR_INTERNAL;
+    }
+    return ISU_RES_OK;
+}
+
+isu_result isu_pkg_get_version(isu_pkg_info pkg_info, char *version, size_t len)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+
+    if (pkg_info == NULL || version == NULL)
+        return ISU_RES_ERR_ARGUMENT;
+
+    struct _isu_pkg_info* pkg_info_i = (struct _isu_pkg_info*)pkg_info;
+
+    if (len < strlen(pkg_info_i->version)) {
+        return ISU_RES_ERR_BUFF_TOO_SMALL;
+    }
+
+    if (snprintf(version, len, "%s", pkg_info_i->version) < strlen(pkg_info_i->version)) {
+        return ISU_RES_ERR_INTERNAL;
+    }
+    return ISU_RES_OK;
+}
+
+isu_result isu_pkg_info_free(isu_pkg_info pkg_info)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+    if (pkg_info == NULL) {
+        goto exit;
+    }
+
+    if (pkg_info == NULL)
+        return ISU_RES_ERR_ARGUMENT;
+
+    struct _isu_pkg_info* pkg_info_i = (struct _isu_pkg_info*)pkg_info;
+
+        if (pkg_info_i != NULL) {
+            for (size_t i = 0; i < pkg_info_i->service_files_len; i++) {
+                free(pkg_info_i->service_files[i]);
+            }
+            free(pkg_info_i->service_files);
+        }
+
+    free(pkg_info_i->name);
+    free(pkg_info_i->version);
+    free(pkg_info_i);
+exit:
+    return ISU_RES_OK;
+}
+
+isu_result isu_install(const char *path)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+
+    if (path == NULL)
+        return ISU_RES_ERR_ARGUMENT;
+
+    return isu_dbus_call("install", path);
+}
+
+isu_result isu_uninstall(const char *path)
+{
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+
+    if (path == NULL)
+        return ISU_RES_ERR_ARGUMENT;
+
+    return isu_dbus_call("uninstall", path);
+}
diff --git a/src/libisu/libisu.h b/src/libisu/libisu.h
new file mode 100644 (file)
index 0000000..5d22ee3
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#pragma once
+#include <sys/types.h>
+
+/**
+ * @addtogroup CAPI_ISU_MODULE
+ * @{
+ */
+
+/**
+ * @brief ISU package list context
+ */
+typedef void* isu_pkg_list;
+
+/**
+ * @brief ISU package info context
+ */
+typedef void* isu_pkg_info;
+
+/**
+ * @brief Result
+ */
+typedef enum {
+       ISU_RES_OK,                 /**< Successful */
+       ISU_RES_ERR_ARGUMENT,       /**< Invalid arguments provided */
+       ISU_RES_ERR_NAME,           /**< Invalid name */
+       ISU_RES_ERR_PATH,           /**< Invalid path */
+       ISU_RES_ERR_NOT_EXIST,      /**< Package not exist */
+       ISU_RES_ERR_NOT_PERMITTED,  /**< No permission to perform the operation */
+       ISU_RES_ERR_INVALID_FORMAT, /**< Invalid or corrupted ISU archive */
+       ISU_RES_ERR_CHECKSUM,       /**< Checksum does not match */
+       ISU_RES_ERR_FEATURE,        /**< Feature not supported */
+       ISU_RES_ERR_INTERNAL,       /**< Internal error */
+       ISU_RES_ERR_BUFF_TOO_SMALL   /**< Provided buffer is too small to store th full content */
+} isu_result;
+
+/**
+ * @platform
+ * @brief Install ISU package
+ * @since_tizen 9.0
+ * @privlevel platform
+ * @privilege http://tizen.org/privilege/isu.admin
+ *
+ *
+ * @param[in] path - Path to the zip archive
+ *
+ * @return #isu_result
+ * @retval #ISU_RES_OK Success
+ * @retval #ISU_RES_ERR_ARGUMENT Invalid argument provided
+ * @retval #ISU_RES_ERR_FEATURE ISU Feature not supported
+ * @retval #ISU_RES_ERR_NOT_PERMITTED No permission to perform the operation
+ * @retval #ISU_RES_ERR_PATH Provided file not exist
+ * @retval #ISU_RES_ERR_INVALID_FORMAT Incorrect or corrupted ISU package
+ * @retval #ISU_RES_ERR_CHECKSUM Incorrect or corrupted ISU package
+ * @retval #ISU_RES_ERR_INTERNAL Internal error
+ */
+isu_result isu_install(const char *path);
+
+/**
+ * @brief Init ISU package list
+ *
+ * @return #isu_pkg_list on success, otherwise NULL
+ *
+ * @code
+ *
+ * void print_isu_pkgs(void) {
+ *     isu_pkg_list pkg_list = isu_list_init();
+ *     if (pkg_list == NULL) {
+ *         printf("isu_list_init error\n");
+ *         return;
+ *     }
+ *
+ *     isu_pkg_info pkg_info = NULL;
+ *
+ *     while ((pkg_info = isu_list_next(isu_pkg_list)) != NULL) {
+ *         char name[256];
+ *         isu_result = isu_pkg_get_name(pkg_info, name, sizeof(name));
+ *         isu_pkg_info_free(isu_result);
+ *
+ *         if (isu_result != ISU_RES_OK) {
+ *             printf("isu_pkg_get_name() error\n");
+ *             return;
+ *         }
+ *         printf("ISU package name: %s\n", name);
+ *     }
+ *
+ *     isu_list_free(pkg_list);
+ * }
+ *
+ * @endcode
+ */
+isu_pkg_list isu_list_init();
+
+/**
+ * @brief Get the next ISU package
+ * @since_tizen 9.0
+ *
+ * @param[in] pkg_list ISU list returned by #isu_list_init
+ *
+ * This function allocates new isu_pkg_list, which should be freed with #isu_list_free
+ *
+ * @return #isu_pkg_info on success, otherwise NULL
+ * @retval #isu_pkg_info
+ */
+isu_pkg_info isu_list_next(isu_pkg_list pkg_list);
+
+/**
+ * @brief Get ISU package name
+ * @since_tizen 9.0
+ *
+ * @param[in] pkg_info ISU package info
+ * @param[out] name buffer for the ISU package name
+ * @param[in] len The name buffer length
+ *
+ * @return #isu_result
+ * @retval #ISU_RES_OK Success
+ * @retval #ISU_RES_ERR_ARGUMENT Invalid argument provided
+ * @retval #ISU_RES_ERR_BUFF_TOO_SMALL Buffer is to small to store the full content
+ * @retval #ISU_RES_ERR_INTERNAL Internal error
+ */
+isu_result isu_pkg_get_name(isu_pkg_info pkg_info, char *name, size_t len);
+
+/**
+ * @brief Get ISU version
+ * @since_tizen 9.0
+ *
+ * @param[in] pkg_info ISU package info
+ * @param[out] version buffer for the ISU package version
+ * @param[in] len The name buffer length
+ *
+ * @return #isu_result
+ * @retval #ISU_RES_OK Success
+ * @retval #ISU_RES_ERR_ARGUMENT Invalid argument provided
+ * @retval #ISU_RES_ERR_INTERNAL Internal error
+ * @retval #ISU_RES_ERR_BUFF_TOO_SMALL Buffer is to small to store the full content
+ * @retval #ISU_RES_ERR_FEATURE ISU Feature not supported
+ */
+isu_result isu_pkg_get_version(isu_pkg_info pkg_info, char *version, size_t len);
+
+/**
+ * @brief Free the ISU package info context
+ * @since_tizen 9.0
+ *
+ * @param[in] pkg_info ISU package info context
+ *
+ * @return #isu_result
+ * @retval #ISU_RES_OK Success
+ * @retval #ISU_RES_ERR_ARGUMENT Invalid argument provided
+ * @retval #ISU_RES_ERR_FEATURE ISU Feature not supported
+ */
+isu_result isu_pkg_info_free(isu_pkg_info pkg_info);
+
+/**
+ * @brief Free the ISU packages list context
+ * @since_tizen 9.0
+ *
+ * @param[in] pkg_list ISU packages list info context
+ *
+ * @return #isu_result
+ * @retval #ISU_RES_OK Success
+ */
+isu_result isu_list_free(isu_pkg_list pkg_list);
+
+/**
+ * @platform
+ * @brief Uninstall ISU package
+ * @since_tizen 9.0
+ * @privlevel platform
+ * @privilege http://tizen.org/privilege/isu.admin
+ *
+ * @param[in] pkg_name ISU package name
+ *
+ * @return #isu_result
+ * @retval #ISU_RES_OK Success
+ * @retval #ISU_RES_ERR_ARGUMENT Invalid argument provided
+ * @retval #ISU_RES_ERR_NOT_EXIST ISU package not installed
+ * @retval #ISU_RES_ERR_FEATURE ISU Feature not supported
+ * @retval #ISU_RES_ERR_NOT_PERMITTED No permission to perform the operation
+ */
+isu_result isu_uninstall(const char *pkg_name);
+
+/**
+ * @}
+ */
diff --git a/src/libisu/libisu.pc.in b/src/libisu/libisu.pc.in
new file mode 100644 (file)
index 0000000..982be81
--- /dev/null
@@ -0,0 +1,14 @@
+# Package Information for pkg-config
+
+prefix=@PC_PREFIX@
+exec_prefix=/usr
+libdir=@PC_LIBDIR@
+includedir=@PC_INCLUDE@
+
+Name: @PC_NAME@
+Description: @PC_DESCRIPTION@
+Version: @PC_VERSION@
+Requires: @PC_REQUIRED@
+Libs: -L${libdir} @PC_LDFLAGS@
+Cflags: -I${includedir}
+