Add crash-service part of the diagnostic API 28/229228/9
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Wed, 18 Mar 2020 11:07:45 +0000 (12:07 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Mon, 27 Jul 2020 09:02:30 +0000 (11:02 +0200)
Change-Id: I869ebed29b6657a2227fae407079c643819a551b

packaging/crash-worker.spec
src/crash-service/CMakeLists.txt
src/crash-service/crash-service.c
src/crash-service/crash-service.conf
src/crash-service/diagnostics/diagnostics-run.conf [new file with mode: 0644]
src/crash-service/diagnostics/diagnostics.c [new file with mode: 0644]
src/crash-service/diagnostics/diagnostics.h [new file with mode: 0644]
src/crash-service/diagnostics/org.tizen.system.diagnostics.service [new file with mode: 0644]

index d0abb12..04e71f1 100644 (file)
@@ -25,6 +25,7 @@ BuildRequires:  pkgconfig(iniparser)
 BuildRequires:  pkgconfig(capi-system-info)
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(rpm)
+BuildRequires:  pkgconfig(libzip)
 BuildRequires:  cmake
 BuildRequires:  pkgconfig(pkgmgr-info)
 BuildRequires:  pkgconfig(libunwind-generic)
@@ -192,6 +193,8 @@ mkdir -p %{buildroot}%{crash_temp}
 %attr(-,root,root) %{_unitdir}/crash-service.service
 %attr(-,root,root) %{_sysconfdir}/dbus-1/system.d/crash-service.conf
 %attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.crash.livedump.service
+%attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.diagnostics.service
+%attr(0644,root,root) %{_libdir}/tmpfiles.d/diagnostics-run.conf
 %endif
 
 #upgrade script
index a8de738..36689d5 100644 (file)
@@ -5,6 +5,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/)
 
 SET(CRASH_SERVICE_SRCS
        crash-service.c
+       diagnostics/diagnostics.c
        )
 
 INCLUDE(GNUInstallDirs)
@@ -14,6 +15,8 @@ INCLUDE(FindPkgConfig)
 pkg_check_modules(crash-service_pkgs REQUIRED
        dlog
        gio-2.0
+       gio-unix-2.0
+       libzip
        )
 
 FOREACH(flag ${crash-service_pkgs_CFLAGS})
@@ -44,6 +47,9 @@ CONFIGURE_FILE(crash-service.pc.in crash-service.pc @ONLY)
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.system.crash.livedump.service
        DESTINATION /usr/share/dbus-1/system-services)
 
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/diagnostics/org.tizen.system.diagnostics.service
+       DESTINATION /usr/share/dbus-1/system-services)
+
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.conf
        DESTINATION /etc/dbus-1/system.d)
 
@@ -60,3 +66,7 @@ INSTALL(TARGETS libcrash-service LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
 
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.pc
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/diagnostics/diagnostics-run.conf
+       DESTINATION ${CMAKE_INSTALL_LIBDIR}/tmpfiles.d/
+       PERMISSIONS OWNER_READ OWNER_WRITE)
index 0917fcb..480253b 100644 (file)
@@ -16,7 +16,9 @@
  * limitations under the License.
  */
 
+#include <assert.h>
 #include <gio/gio.h>
+#include <gio/gunixfdlist.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <sys/select.h>
 #include "crash-manager/crash-manager.h"
 #include "libcrash-service.h"
 #include "shared/log.h"
+#include "diagnostics/diagnostics.h"
 
 /* Dbus activation */
 #define CRASH_BUS_NAME          "org.tizen.system.crash.livedump"
 #define CRASH_OBJECT_PATH       "/Org/Tizen/System/Crash/Livedump"
+#define CRASH_INTERFACE_NAME    CRASH_BUS_NAME
+
+#define DIAGNOSTICS_BUS_NAME        "org.tizen.system.diagnostics"
+#define DIAGNOSTICS_OBJECT_PATH     "/Org/Tizen/System/Diagnostics"
+#define DIAGNOSTICS_INTERFACE_NAME  DIAGNOSTICS_BUS_NAME
 
 #define TIMEOUT_INTERVAL_SEC 60
 #define TIMEOUT_LIVEDUMP_SEC 50
@@ -44,7 +52,7 @@
 static GMainLoop *loop;
 static GMutex timeout_mutex;
 static guint timeout_id;
-static GDBusNodeInfo *introspection_data;
+static GDBusNodeInfo *introspection_data, *introspection_diagnostics_data;
 static const gchar introspection_xml[] =
 "<node>"
 " <interface name='org.tizen.system.crash.livedump'>"
@@ -56,6 +64,17 @@ static const gchar introspection_xml[] =
 " </interface>"
 "</node>";
 
+static const gchar introspection_diagnostics_xml[] =
+"<node>"
+" <interface name='org.tizen.system.diagnostics'>"
+"  <method name='get_file'>"
+"   <arg type='s' name='report_path' direction='in'/>"
+"   <arg type='i' name='type' direction='in'/>"
+"   <arg type='i' name='result' direction='out'/>"
+"  </method>"
+" </interface>"
+"</node>";
+
 void child_exit(int sig)
 {
        wait(NULL);
@@ -219,17 +238,16 @@ static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, g
        }
 }
 
-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)
+static void crash_interface_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();
-
+       _D("crash iface method: %s", method_name);
        if (g_strcmp0(method_name, "livedump_pid") == 0) {
                gchar *dump_reason;
                const pid_t pid;
@@ -246,6 +264,112 @@ static void method_call_handler(GDBusConnection *conn,
                }
        }
 
+}
+
+static GUnixFDList* prepare_unix_fd_list(int fd)
+{
+       assert(fd >= 0);
+
+       GError *error = NULL;
+       GUnixFDList *fd_list = g_unix_fd_list_new();
+
+       assert(fd_list);
+
+       if (g_unix_fd_list_append(fd_list, fd, &error) == -1) {
+               _E("g_unix_fd_list_append error: %s\n", error ? error->message : "unspecified error");
+               return NULL;
+       }
+       return fd_list;
+}
+
+static void diagnostics_get_file_handler(GDBusConnection *conn,
+                                      GDBusMethodInvocation *invocation,
+                                      const gchar *report_path,
+                                      enum diagnostics_entry entry)
+{
+       int fd = diagnostics_report_get_file(report_path, entry);
+       if (fd < 0) {
+               g_dbus_method_invocation_return_error(invocation,
+                                                     BR_ERROR,
+                                                     -fd,
+                                                     "report_get_file() failed: %d", fd);
+               return;
+       }
+
+       GUnixFDList *fd_list = prepare_unix_fd_list(fd);
+       if (fd_list == NULL) {
+               close(fd);
+               g_dbus_method_invocation_return_error(invocation,
+                                                     BR_ERROR,
+                                                     BR_ERR_INTERNAL,
+                                                     "Internal error");
+               return;
+       }
+
+       GDBusMessage *incoming_message = g_dbus_method_invocation_get_message(invocation);
+       GDBusMessage *message = g_dbus_message_new_method_reply(incoming_message);
+       g_dbus_message_set_unix_fd_list(message, fd_list);
+
+       GError *error = NULL;
+       if (!g_dbus_connection_send_message(conn,
+                                           message,
+                                           G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                           NULL,
+                                           &error)) {
+               _E("Send reply message error: %s\n", error ? error->message : "");
+               if (error)
+                       g_error_free(error);
+       }
+
+       g_object_unref(message);
+}
+
+static void diagnostics_interface_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)
+{
+       _D("some diagnostics method xD: %s", method_name);
+       if (g_strcmp0(method_name, "get_file") == 0) {
+               if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(si)"))) {
+                       gchar *report_path;
+                       enum diagnostics_entry entry;
+                       g_variant_get(parameters, "(si)", &report_path, &entry);
+                       diagnostics_get_file_handler(conn, invocation, report_path, entry);
+               } else {
+                       _E("Parameters are not of the correct type (si)");
+                       g_dbus_method_invocation_return_error(invocation,
+                                                             CS_ERROR,
+                                                             CS_ERR_PARAM,
+                                                             "Parameters are not of the correct type (is)");
+               }
+       }
+       _D("after return");
+}
+
+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();
+
+       _D("iface name: %s method: %s", iface_name, method_name);
+       if (g_strcmp0(iface_name, CRASH_INTERFACE_NAME) == 0)
+               crash_interface_handler(conn, sender, object_path, iface_name, method_name, parameters, invocation, user_data);
+       else if (g_strcmp0(iface_name, DIAGNOSTICS_INTERFACE_NAME) == 0)
+               diagnostics_interface_handler(conn, sender, object_path, iface_name, method_name, parameters, invocation, user_data);
+       else
+               _E("unsuppoerted interface: %s", iface_name);
+
        add_timeout();
 }
 
@@ -259,15 +383,28 @@ 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, CRASH_BUS_NAME) == 0) {
+               object_path = CRASH_OBJECT_PATH;
+               interface_info = introspection_data->interfaces[0];
+       } else if (strcmp(name, DIAGNOSTICS_BUS_NAME) == 0) {
+               object_path = DIAGNOSTICS_OBJECT_PATH;
+               interface_info = introspection_diagnostics_data->interfaces[0];
+       } else {
+               _E("on_bus_acquired: unknown name: %s", name);
+               return;
+       }
+
        registration_id = g_dbus_connection_register_object(conn,
-                               CRASH_OBJECT_PATH,
-                               introspection_data->interfaces[0],
+                               object_path,
+                               interface_info,
                                &interface_vtable, NULL, NULL, &error);
        if (registration_id == 0 || error) {
-               _E("Failed to g_dbus_connection_register_object: %s", error ? error->message : "");
+               _E("Failed to g_dbus_connection_register_object for %s: %s", name, error ? error->message : "");
                if (error)
                        g_error_free(error);
        }
@@ -298,6 +435,13 @@ static bool dbus_init(void)
                return false;
        }
 
+       introspection_diagnostics_data =
+               g_dbus_node_info_new_for_xml(introspection_diagnostics_xml, NULL);
+       if (introspection_diagnostics_data == NULL) {
+               _E("Failed to init g_dbus_info_new_for_xml for diagnostics");
+               return false;
+       }
+
        GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
        if (!conn || error) {
                _E("Failed to get dbus: %s", error ? error->message : "");
@@ -310,6 +454,10 @@ static bool dbus_init(void)
                       G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
                       on_name_acquired, on_name_lost, NULL, NULL);
 
+       g_bus_own_name(G_BUS_TYPE_SYSTEM, DIAGNOSTICS_BUS_NAME,
+                      G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
+                      on_name_acquired, on_name_lost, NULL, NULL);
+
        return true;
 }
 
index dd38a30..3cde551 100644 (file)
@@ -2,12 +2,24 @@
  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
 <busconfig>
        <policy user="root">
+               <allow own="org.tizen.system.diagnostics"/>
+               <allow send_destination="org.tizen.system.diagnostics"
+                      send_interface="org.tizen.system.diagnostics"
+                      send_member="get_file"/>
+       </policy>
+       <policy user="root">
                <allow own="org.tizen.system.crash.livedump"/>
                <allow send_destination="org.tizen.system.crash.livedump"
                       send_interface="org.tizen.system.crash.livedump"
                       send_member="livedump_pid"/>
        </policy>
        <policy user="crash_worker">
+               <allow own="org.tizen.system.diagnostics"/>
+               <allow send_destination="org.tizen.system.diagnostics"
+                      send_interface="org.tizen.system.diagnostics"
+                      send_member="get_file"/>
+       </policy>
+       <policy user="crash_worker">
                <allow own="org.tizen.system.crash.livedump"/>
                <allow send_destination="org.tizen.system.crash.livedump"
                       send_interface="org.tizen.system.crash.livedump"
        </policy>
        <policy context="default">
                <deny own="org.tizen.system.crash.livedump"/>
+               <deny own="org.tizen.system.diagnostics"/>
                <check privilege="http://tizen.org/privilege/internal/livecoredump"
                       send_destination="org.tizen.system.crash.livedump"
                       send_interface="org.tizen.system.crash.livedump"
                       send_member="livedump_pid"/>
+               <deny send_destination="org.tizen.system.diagnostics"/>
                <deny send_destination="org.tizen.system.crash.livedump"/>
+               <check send_destination="org.tizen.system.diagnostics"
+                      send_interface="org.tizen.system.diagnostics"
+                      send_member="get_file"
+                      privilege="http://tizen.org/privilege/internal/default/platform"/>
        </policy>
 </busconfig>
diff --git a/src/crash-service/diagnostics/diagnostics-run.conf b/src/crash-service/diagnostics/diagnostics-run.conf
new file mode 100644 (file)
index 0000000..cfdd4c4
--- /dev/null
@@ -0,0 +1,6 @@
+d      /run/diagnostics        0755    log     log     -       -
+d      /run/diagnostics/priv   0700    log     log     -       -
+t      /run/diagnostics/priv   0700    log     log     -       security.SMACK64=System
+d      /run/diagnostics/priv/fifo      0700    log     log     -       -
+t      /run/diagnostics/priv/fifo      0700    log     log     -       security.SMACK64TRANSMUTE=TRUE
+t      /run/diagnostics/priv/fifo      0700    log     log     -       security.SMACK64=User::App::Shared
diff --git a/src/crash-service/diagnostics/diagnostics.c b/src/crash-service/diagnostics/diagnostics.c
new file mode 100644 (file)
index 0000000..f5dc1a1
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * crash-manager
+ *
+ * Copyright (c) 2020 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 <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zip.h>
+#include <errno.h>
+#include <dirent.h>
+#include "diagnostics/diagnostics.h"
+
+#define LOG_TAG "CRASH_MANAGER"
+
+#include "defs.h"
+#include "shared/log.h"
+#include "shared/util.h"
+#include "shared/config.h"
+
+#define SHARE_BASE_DIR    "/run/diagnostics/priv/fifo"
+#define TMP_FILE_TEMPLATE "file_XXXXXX"
+
+#define JSON_FILE_EXT     ".json"
+#define ZIP_FILE_EXT      ".zip"
+
+enum diagnostics_type {
+       BT_UNKNOWN,
+       BT_FULL_REPORT,
+       BT_INFO_JSON,
+       BT_DIR
+};
+
+static char *type_to_str(enum diagnostics_entry entry) {
+       switch (entry) {
+       case FULL_REPORT:
+               return "FULL_REPORT";
+       case INFO_JSON:
+               return "INFO_JSON";
+       default:
+               return "UKNOWN";
+       }
+}
+
+static char *get_temp_file_name()
+{
+       char *result;
+       if (asprintf(&result, "%s/%s", SHARE_BASE_DIR, TMP_FILE_TEMPLATE) == -1) {
+               _E("asprintf() error: %m\n");
+               return NULL;
+       }
+
+       if (mktemp(result) == NULL || result[0] == '\0') {
+               _E("mktemp() error: %m\n");
+               free(result);
+               return NULL;
+       }
+
+       return result;
+}
+
+static bool is_report_file(const char *path, const char *reports_dir)
+{
+       assert(path);
+       assert(reports_dir);
+
+       char *r_path = realpath(path, NULL);
+       if (r_path == NULL) {
+               _E("realpath() for %s error: %m\n", path);
+               return false;
+       }
+
+       if (access(r_path, F_OK) == -1) {
+               _E("access() for %s error: %m\n", path);
+               return false;
+       }
+
+       char *start = strstr(r_path, reports_dir);
+
+       if (start == NULL) {
+               _I("%s is not within %s\n", r_path, reports_dir);
+               return false;
+       }
+
+       return true;
+}
+
+static enum diagnostics_type check_report_type(const char *path)
+{
+       assert(path);
+
+       if (strstr(path, JSON_FILE_EXT) == &path[strlen(path) - strlen(JSON_FILE_EXT)]) {
+               _D("Report type of %s is JSON_FILE\n", path);
+               return BT_INFO_JSON;
+       }
+
+       if (strstr(path, ZIP_FILE_EXT) == &path[strlen(path) - strlen(ZIP_FILE_EXT)]) {
+               _D("Report type of %s is FULL_REPORT\n", path);
+               return BT_FULL_REPORT;
+       }
+
+       struct stat file_stat;
+       if (stat(path, &file_stat) == -1) {
+               _E("stat() for %s error: %m\n", path);
+               return BT_UNKNOWN;
+       }
+
+       if (stat(path, &file_stat) == 0 && S_ISDIR(file_stat.st_mode)) {
+               _D("Report type of %s is DIR", path);
+               return BT_DIR;
+       }
+
+       _I("Report type of %s is UNKNOWN", path);
+       return BT_UNKNOWN;
+}
+
+static bool ends_with(const char *string, const char *end)
+{
+       assert(string);
+       assert(end);
+
+       ssize_t end_len = strlen(end);
+       ssize_t string_len = strlen(string);
+
+       if (end_len == 0 || end_len > string_len)
+               return false;
+
+       return strcasecmp(&string[string_len - end_len], end) == 0;
+}
+
+static bool file_match(const char *file_name, enum diagnostics_entry entry)
+{
+       assert(file_name);
+
+       switch(entry) {
+       case INFO_JSON:
+               return ends_with(file_name, JSON_FILE_EXT);
+       default:
+               return false;
+       }
+}
+
+static bool extract_file(zip_t *archive, int index, const char *out_file_name)
+{
+       bool result = false;
+       zip_file_t *zfile = NULL;
+
+       int extracted_fd = open(out_file_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+       if (extracted_fd < 0) {
+               _E("open() file \"%s\" for write error: %m\n", out_file_name);
+               goto out;
+       }
+
+       struct zip_stat stat;
+       if (zip_stat_index(archive, index, ZIP_FL_UNCHANGED, &stat) == -1) {
+               printf("zip_stat_index() error\n");
+               goto out;
+       }
+
+       zfile = zip_fopen_index(archive, index, ZIP_FL_UNCHANGED);
+       if (zfile == NULL) {
+               _E("zip_fopen_index() error\n");
+               goto out;
+       }
+
+       char buff[0x1000];
+       zip_uint64_t sum = 0;
+
+       while (sum < stat.size) {
+               zip_int64_t len = zip_fread(zfile, buff, sizeof(buff));
+               if (len < 0) {
+                       _E("zip_fread() error\n");
+                       goto out;
+               }
+               if (write(extracted_fd, buff, len) == -1) {
+                       _E("write() error: %m\n");
+                       goto out;
+               }
+               sum += len;
+       }
+
+       result = true;
+out:
+       if (zfile != NULL)
+               zip_fclose(zfile);
+
+       if (extracted_fd >= 0)
+               close(extracted_fd);
+
+       return result;
+}
+
+static int search_file_in_zip(zip_t *archive, enum diagnostics_entry entry)
+{
+       zip_int64_t count = zip_get_num_entries(archive, ZIP_FL_UNCHANGED);
+
+       for (zip_int64_t i = 0; i < count; i++) {
+               const char *entry_name = zip_get_name(archive, i, ZIP_FL_ENC_GUESS);
+               if (entry_name == NULL) {
+                       _E("zip_get_name() error\n");
+                       continue;
+               }
+
+               if (!file_match(entry_name, entry))
+                       continue;
+
+               return i;
+       }
+       return -1;
+}
+
+static int share_compressed_file(const char *path, enum diagnostics_entry entry)
+{
+       assert(path);
+
+       int err = 0;
+       int fd = -BR_ERR_INTERNAL;
+
+       char *out_file = get_temp_file_name();
+
+       if (out_file == NULL)
+               return -BR_ERR_INTERNAL;
+
+       zip_t *archive = zip_open(path, ZIP_RDONLY, &err);
+       if (err != 0) {
+               _E("zip_open() %s error: %d\n", path, err);
+               archive = NULL;
+               goto out;
+       }
+
+       int file_index = search_file_in_zip(archive, entry);
+       if (file_index < 0) {
+               _I("Archive \"%s\" doesn't contain file type: %s", path, type_to_str(entry));
+               fd = -BR_ERR_PARAM;
+               goto out;
+       }
+
+       if (extract_file(archive, file_index, out_file)) {
+               fd = open(out_file, O_RDONLY);
+               if (fd == -1) {
+                       _E("open() file \"%s\" for read error: %m", out_file);
+                       goto out;
+               }
+               _D("share file %s from \"%s\" as \"%s\"", type_to_str(entry), path, out_file);
+       }
+
+out:
+       if (archive)
+               zip_close(archive);
+       unlink(out_file);
+       free(out_file);
+       return fd;
+}
+
+static int share_file(const char *path)
+{
+       assert(path);
+
+       const char *dest_file = get_temp_file_name();
+
+       if (dest_file == NULL)
+               return -BR_ERR_INTERNAL;
+
+       _D("share file \"%s\" as \"%s\"", path, dest_file);
+
+       if (copy_file(dest_file, path) == -1) {
+               _E("copy_file() error");
+               return -BR_ERR_INTERNAL;
+       }
+
+       int fd = open(dest_file, O_RDONLY);
+       if (fd  == -1) {
+               _E("open() file: \"%s\" error: %m\n", path);
+               return -BR_ERR_INTERNAL;
+       }
+       unlink(dest_file);
+
+       return fd;
+}
+
+static int share_uncompressed_file(const char *path, enum diagnostics_entry entry)
+{
+       assert(path);
+
+       struct dirent *ent;
+       DIR *dir = NULL;
+       int result = -BR_ERR_PARAM;
+       if ((dir = opendir(path)) == NULL) {
+               _E("opendir() for \"%s\" error: %m\n", path);
+               result = -BR_ERR_INTERNAL;
+               goto out;
+       }
+
+       while((ent = readdir(dir)) != NULL) {
+               if (file_match(ent->d_name, entry)) {
+                       char file_path[PATH_MAX];
+                       int ret = snprintf(file_path, PATH_MAX, "%s/%s", path, ent->d_name);
+
+                       if (ret < 0) {
+                               _E("sprintf() error: %m");
+                               result = -BR_ERR_INTERNAL;
+                               goto out;
+                       } else if (ret >= PATH_MAX) {
+                               _E("sprintf(): result string is too long");
+                               result = -BR_ERR_INTERNAL;
+                               goto out;
+                       }
+                       result = share_file(file_path);
+                       break;
+               }
+       }
+
+       if (result < 0)
+               _I("Report \"%s\" doesn't contain requested entry type.", path);
+
+out:
+       if (dir != NULL)
+               closedir(dir);
+
+       return result;
+}
+
+static int share_simple_file(const char *path, enum diagnostics_entry entry)
+{
+       assert(path);
+
+       if (entry != INFO_JSON) {
+               _E("Unsupported file type for report \"%s\" (%s)\n", path, type_to_str(entry));
+               return -BR_ERR_PARAM;
+       }
+
+       return share_file(path);
+}
+
+int diagnostics_report_get_file(const char *report_path,
+                             const enum diagnostics_entry entry)
+{
+       assert(report_path);
+
+       config_t config;
+       if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH))
+               return -BR_ERR_INTERNAL;
+
+       bool is_report = is_report_file(report_path, config.crash_root_path);
+       config_free(&config);
+
+       if (!is_report) {
+               _E("File \"%s\" doesn't look like a report.", report_path);
+               return -BR_ERR_PARAM;
+       }
+
+       enum diagnostics_type rtype = check_report_type(report_path);
+
+       switch(rtype) {
+       case BT_INFO_JSON:
+               return share_simple_file(report_path, entry);
+               break;
+       case BT_FULL_REPORT:
+               if (entry == FULL_REPORT)
+                       return share_file(report_path);
+               return share_compressed_file(report_path, entry);
+               break;
+       case BT_DIR:
+               return share_uncompressed_file(report_path, entry);
+               break;
+       default:
+               _E("%s is an unsupported report file.", report_path);
+               return -BR_ERR_PARAM;
+       }
+}
diff --git a/src/crash-service/diagnostics/diagnostics.h b/src/crash-service/diagnostics/diagnostics.h
new file mode 100644 (file)
index 0000000..8b2bea9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * crash-manager
+ *
+ * Copyright (c) 2020 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.
+ */
+#ifndef __DIAGNOSTICS_H__
+#define __DIAGNOSTICS_H__
+
+#define BR_ERROR        1
+#define BR_OK           0
+#define BR_ERR_PARAM    1
+#define BR_ERR_INTERNAL 2
+
+enum diagnostics_entry {
+       FULL_REPORT = 0,
+       INFO_JSON
+};
+
+enum diagnostics_metadata {
+       APPID,
+       CMDLINE,
+       SIGNAL,
+};
+
+struct diagnostics_report;
+
+int diagnostics_report_get_file(const char *report_path,
+                             const enum diagnostics_entry entry);
+
+#endif  // __DIAGNOSTICS_H__
diff --git a/src/crash-service/diagnostics/org.tizen.system.diagnostics.service b/src/crash-service/diagnostics/org.tizen.system.diagnostics.service
new file mode 100644 (file)
index 0000000..621ac20
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.tizen.system.diagnostics
+Exec=/bin/false
+SystemdService=crash-service.service