bugreport-service: Independence from the crash-manager 68/255068/3
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Wed, 10 Mar 2021 10:30:39 +0000 (11:30 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Fri, 12 Mar 2021 11:16:34 +0000 (12:16 +0100)
The changes that this commit makes allow to install the
bugreport-service without having to install crash-manager.

Change-Id: If448436c0dff6a69f1bef6b4d249c199f549dec8

CMakeLists.txt
include/defs.h.in
src/bugreport-service/CMakeLists.txt
src/bugreport-service/bugreport-service.c
src/bugreport-service/systemdump.c [new file with mode: 0644]
src/bugreport-service/systemdump.h [new file with mode: 0644]
src/crash-manager/crash-manager.c
src/shared/util.c
src/shared/util.h
tests/system/CMakeLists.txt

index 3893dad..0288a17 100644 (file)
@@ -5,6 +5,10 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX})
 
 ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1)
 
+IF("${LIVEDUMPER}" STREQUAL "ON")
+       ADD_DEFINITIONS(-DLIVEDUMPER=ON)
+ENDIF()
+
 # Sub modules
 ADD_SUBDIRECTORY(include)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src/bugreport-service)
@@ -19,9 +23,6 @@ IF("${LIVEDUMPER}" STREQUAL "ON")
 ENDIF()
 
 IF("${CRASH_SERVICE}" STREQUAL "ON")
-       if (NOT "${LIVEDUMPER}" STREQUAL "ON")
-               message(FATAL_ERROR "Livedumper is required to build bugreport-service")
-       ENDIF()
        ADD_SUBDIRECTORY(src/bugreport-service)
 ENDIF()
 
index 906b649..ad5ca3a 100644 (file)
@@ -21,4 +21,9 @@
 #define LIVEDUMPER_BIN_PATH            "@LIVEDUMPER_BIN_PATH@"
 #define DLOG_LOG_LEVEL                 @DLOG_LOG_LEVEL@
 
+#define CRASH_TEMP_SUBDIR               "/temp/"
+#define CRASH_PATH_SUBDIR               "/dump/"
+#define LIVE_PATH_SUBDIR                "/livedump/"
+#define APP_PATH_SUBDIR                 "/app/"
+
 #endif /* __DEFS_H__ */
index 4bdeb18..4d7c849 100644 (file)
@@ -5,8 +5,10 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/)
 
 SET(CRASH_SERVICE_SRCS
        bugreport-service.c
+       systemdump.c
        diagnostics/diagnostics.c
        ${CMAKE_SOURCE_DIR}/src/shared/util.c
+       ${CMAKE_SOURCE_DIR}/src/shared/spawn.c
        ${CMAKE_SOURCE_DIR}/src/shared/config.c
        )
 
index b869140..e5d2d2d 100644 (file)
@@ -34,6 +34,7 @@
 #include "shared/log.h"
 #include "shared/util.h"
 #include "diagnostics/diagnostics.h"
+#include "systemdump.h"
 
 /* Dbus activation */
 #define CRASH_BUS_NAME          "org.tizen.system.crash.livedump"
@@ -52,6 +53,7 @@
 #define CS_ERR_TIMEOUT     2
 #define CS_ERR_READ        3
 #define CS_ERR_INTERNAL    4
+#define CS_ERR_UNSUPPORTED 5
 
 static GMainLoop *loop;
 static GMutex timeout_mutex;
@@ -117,7 +119,7 @@ static void remove_timeout(void)
        _D("Remove loop timeout");
 }
 
-struct livedump_cb_data {
+struct callback_data {
        int read_fd;
        GDBusMethodInvocation *invocation;
        GSource* source;
@@ -138,7 +140,7 @@ static bool data_ready(int fd)
 
 static gboolean read_result_cb(gpointer data)
 {
-       struct livedump_cb_data *cb_data = (struct livedump_cb_data*)data;
+       struct callback_data *cb_data = (struct callback_data*)data;
        char report_path[PATH_MAX] = {0};
 
        if (!data_ready(cb_data->read_fd)) {
@@ -170,26 +172,27 @@ end:
        return G_SOURCE_REMOVE;
 }
 
-static bool livedump_run(int write_fd, pid_t pid, const gchar *dump_reason)
+static bool livedump_run(struct dump_operation_data *data)
 {
+       assert(data);
        char report_path[PATH_MAX];
 
-       if (!crash_manager_livedump_pid(pid, dump_reason, report_path, sizeof(report_path))) {
+       if (!crash_manager_livedump_pid(data->pid, data->dump_reason, report_path, sizeof(report_path))) {
                _E("crash_manager_livedump_pid error");
                return false;
        }
 
-       if (write(write_fd, report_path, strlen(report_path) + 1) == -1) {
+       if (write(data->write_fd, report_path, strlen(report_path) + 1) == -1) {
                _E("Write report_path error: %m");
-               close(write_fd);
+               close(data->write_fd);
                return false;
        }
 
-       close(write_fd);
+       close(data->write_fd);
        return true;
 }
 
-static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, gchar *dump_reason)
+static void dump_handler(GDBusMethodInvocation *invocation, bool (*operation)(struct dump_operation_data *), struct dump_operation_data *data)
 {
        /*
         * We want to run the livedump in different process so as not to
@@ -207,7 +210,7 @@ static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, g
                return;
        }
 
-       struct livedump_cb_data *cb_data = (struct livedump_cb_data*)malloc(sizeof(struct livedump_cb_data));
+       struct callback_data *cb_data = (struct callback_data*)malloc(sizeof(struct callback_data));
        if (cb_data == NULL) {
                _E("cb_data malloc() error: %m");
                exit(EXIT_FAILURE);
@@ -224,7 +227,8 @@ static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, g
        pid_t child_pid = fork();
        if (child_pid == 0) {
                close(fds[0]);
-               if (livedump_run(fds[1], pid, dump_reason))
+               data->write_fd = fds[1];
+               if (operation(data))
                        exit(EXIT_SUCCESS);
                else
                        exit(EXIT_FAILURE);
@@ -251,14 +255,29 @@ static void crash_interface_handler(GDBusConnection *conn,
                                    GDBusMethodInvocation *invocation,
                                    gpointer user_data)
 {
-       _D("crash iface method: %s", method_name);
        if (g_strcmp0(method_name, "livedump_pid") == 0) {
                gchar *dump_reason;
                pid_t pid;
 
                if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(is)"))) {
                        g_variant_get(parameters, "(is)", &pid, &dump_reason);
-                       livedump_pid_handler(invocation, pid, dump_reason);
+                       struct dump_operation_data data = { .write_fd = -1, .pid = pid, .dump_reason = dump_reason };
+
+                       if (pid <= 0) {
+                               dump_handler(invocation, &systemstate_dump_run, &data);
+                       } else {
+#ifdef LIVEDUMPER
+                               if (!have_livecoredumper()) {
+                                       _E("livecoredumper not available - can not provide livedump API. Terminating.");
+                                       goto unsupported;
+                               }
+
+                               dump_handler(invocation, &livedump_run, &data);
+#else
+                               _W("Unsupported livedump_pid() call with PID=%d. No livedumper support for processes.", pid);
+                               goto unsupported;
+#endif
+                       }
                } else {
                        _E("Parameters are not of the correct type (is)");
                        g_dbus_method_invocation_return_error(invocation,
@@ -268,6 +287,12 @@ static void crash_interface_handler(GDBusConnection *conn,
                }
        }
 
+       return;
+unsupported:
+       g_dbus_method_invocation_return_error(invocation,
+                                             CS_ERROR,
+                                             CS_ERR_UNSUPPORTED,
+                                             "Unsupported");
 }
 
 static GUnixFDList* prepare_unix_fd_list(int fd)
@@ -470,11 +495,6 @@ int main(void)
        /* Have consinsent umask across invocations - from shell, bugreport-service, kernel */
        umask(DEFAULT_UMASK);
 
-       if (!have_livecoredumper()) {
-               _E("livecoredumper not available - can not provide livedump API. Terminating.\n");
-               return EXIT_FAILURE;
-       }
-
        loop = g_main_loop_new(NULL, false);
 
        if (!dbus_init()) {
diff --git a/src/bugreport-service/systemdump.c b/src/bugreport-service/systemdump.c
new file mode 100644 (file)
index 0000000..012bcc9
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * systemdump
+ *
+ * Copyright (c) 2021 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 <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "systemdump.h"
+#include "defs.h"
+#include "shared/spawn.h"
+#include "shared/util.h"
+#include "shared/log.h"
+
+#define DUMP_SYSTEMSTATE_TIMEOUT_MS 1000*60*2
+
+struct systemstate_info {
+       char *report_name;
+       char *log_path;
+       char *zip_path;
+       char *temp_path;
+       char *temp_report_dir;
+       char *reports_dir;
+       char *report_path;
+};
+
+static void systemstate_info_init(struct systemstate_info *sinfo)
+{
+       sinfo->report_name = NULL;
+       sinfo->log_path = NULL;
+       sinfo->zip_path = NULL;
+       sinfo->temp_path = NULL;
+       sinfo->temp_report_dir = NULL;
+       sinfo->reports_dir = NULL;
+       sinfo->report_path = NULL;
+}
+
+static void systemstate_info_free(struct systemstate_info *sinfo)
+{
+       free(sinfo->report_name);
+       free(sinfo->log_path);
+       free(sinfo->zip_path);
+       free(sinfo->temp_path);
+       free(sinfo->temp_report_dir);
+       free(sinfo->reports_dir);
+       free(sinfo->report_path);
+}
+
+static bool prepare_paths(struct systemstate_info *sinfo)
+{
+       char date[16];
+
+       struct tm loc_tm;
+       time_t time_info = time(NULL);
+       localtime_r(&time_info, &loc_tm);
+       strftime(date, sizeof(date), "%Y%m%d%H%M%S", &loc_tm);
+
+       if (!make_dir(CRASH_TEMP, DEFAULT_CRASH_DIR_PERM))
+               return false;
+
+       if (asprintf(&sinfo->temp_path, "%s/crash.XXXXXX", CRASH_TEMP) == -1)
+               goto out_of_memory;
+
+       if (mkdtemp(sinfo->temp_path) == NULL || access(sinfo->temp_path, F_OK)) {
+               _E("Failed to create temporary directory %s: %m", sinfo->temp_path);
+               return false;
+       }
+
+       if (asprintf(&sinfo->report_name, "system_0_%s", date) == -1 ||
+           asprintf(&sinfo->temp_report_dir, "%s/%s", sinfo->temp_path, sinfo->report_name) == -1 ||
+           asprintf(&sinfo->log_path, "%s/%s.log", sinfo->temp_report_dir, sinfo->report_name) == -1 ||
+           asprintf(&sinfo->reports_dir, "%s%s", CRASH_ROOT_PATH, LIVE_PATH_SUBDIR) == -1 ||
+           asprintf(&sinfo->zip_path, "%s/report.zip", sinfo->temp_path) == -1 ||
+           asprintf(&sinfo->report_path, "%s%s.%s", sinfo->reports_dir, sinfo->report_name, "zip") == -1)
+               goto out_of_memory;
+
+       if (!make_dir(sinfo->temp_report_dir, 0775)) {
+               _E("Failed to make_dir %s: %m", sinfo->temp_report_dir);
+               return false;
+       }
+
+       if (!make_dir(sinfo->reports_dir, DEFAULT_CRASH_DIR_PERM)) {
+               _E("Failed to make_dir %s: %m", sinfo->reports_dir);
+               return false;
+       }
+
+       return true;
+
+out_of_memory:
+       _E("Out of memory");
+       return false;
+}
+
+static void systemstate_dump(struct systemstate_info *sinfo)
+{
+       char *av[] = {DUMP_SYSTEMSTATE_BIN_PATH, "-d", "-k", "-j", "-p", "-e", "-f", sinfo->log_path, "-b", NULL};
+       spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
+       (void)spawn_wait(av, NULL, &param, DUMP_SYSTEMSTATE_TIMEOUT_MS, NULL);
+}
+
+static void zip_report(struct systemstate_info *sinfo)
+{
+       char *args[] = {"/bin/zip", "-qyr", sinfo->zip_path, sinfo->report_name, NULL};
+       spawn_param_s param1 = { .fn = spawn_nullstdfds };
+       spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = sinfo->temp_path, .next = &param1 };
+       (void)spawn_wait(args, NULL, &param0, DEFAULT_COMMAND_TIMEOUT_MS, NULL);
+}
+
+static void write_reason(struct systemstate_info *sinfo, const char *reason)
+{
+       char *reason_name;
+
+       if (asprintf(&reason_name, "%s.dump_reason", sinfo->report_name) == -1) {
+               _E("Failed to asprintf for reason_name: %m");
+       } else {
+               write_to_file(reason, sinfo->temp_report_dir, reason_name);
+               free(reason_name);
+       }
+}
+
+static bool move_report(struct systemstate_info *sinfo)
+{
+       bool is_ok = true;
+       int fdlock = lock_dir(sinfo->reports_dir, false);
+
+       if (fdlock < 0)
+               return false;
+
+       if (rename(sinfo->zip_path, sinfo->report_path) == -1) {
+               _E("Rename %s to %s error: %m", sinfo->zip_path, sinfo->report_path);
+               is_ok = false;
+       }
+
+       unlock_dir(fdlock);
+       return is_ok;
+}
+
+bool systemstate_dump_run(struct dump_operation_data *data)
+{
+       bool is_ok = true;
+
+       struct systemstate_info sinfo;
+       systemstate_info_init(&sinfo);
+       if (!prepare_paths(&sinfo)) {
+               is_ok = false;
+               goto exit;
+       }
+
+       systemstate_dump(&sinfo);
+       write_reason(&sinfo, data->dump_reason);
+       zip_report(&sinfo);
+
+       if (!move_report(&sinfo)) {
+               is_ok = false;
+               goto exit;
+       }
+
+       if (write(data->write_fd, sinfo.report_path, strlen(sinfo.report_path)) == -1) {
+               _E("Write report_path error: %m");
+               is_ok = false;
+               goto exit;
+       }
+
+exit:
+       close(data->write_fd);
+       remove_dir(sinfo.temp_path, true);
+       systemstate_info_free(&sinfo);
+       return is_ok;
+}
diff --git a/src/bugreport-service/systemdump.h b/src/bugreport-service/systemdump.h
new file mode 100644 (file)
index 0000000..72172f3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * systemdump
+ *
+ * Copyright (c) 2021 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 __SYSTEMDUMP_H
+#define __SYSTEMDUMP_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct dump_operation_data {
+       int write_fd;
+       pid_t pid;
+       const char *dump_reason;
+};
+
+bool systemstate_dump_run(struct dump_operation_data *dump);
+
+#endif  // __SYSTEMDUMP_H
index 1db35b9..c6e0eb0 100644 (file)
 /* Parsing */
 #define KEY_MAX              255
 
-#define CRASH_TEMP_SUBDIR    "/temp/"
-#define CRASH_PATH_SUBDIR    "/dump/"
-#define LIVE_PATH_SUBDIR     "/livedump/"
-#define APP_PATH_SUBDIR      "/app/"
-
 #define WAIT_FOR_OPT_TIMEOUT_SEC 60
 #define SPACE_REQUIRED_KB        1024
 #define HASH_LEN_MAX             0x100
@@ -247,43 +242,13 @@ static int prepare_paths(struct crash_info* cinfo)
        return 1;
 }
 
-static void unlock_dir(int fd)
-{
-       if (flock(fd, LOCK_UN) < 0)
-               _E("Failed to unlock file descriptor: %m");
-       close(fd);
-}
-
 static bool make_dump_dir(void)
 {
        const char *dirs[] = {crash_dump_path, crash_temp_path};
 
        for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) {
-               const char *dirname = dirs[i];
-
-               int r = mkdir(dirname, 0775);
-
-               if (r < 0 && errno != EEXIST) {
-                       _E("Unable to create directory %s: %m", dirname);
+               if (!make_dir(dirs[i], DEFAULT_CRASH_DIR_PERM))
                        return false;
-               }
-
-               chmod(dirname, DEFAULT_CRASH_DIR_PERM); // Fixup permissions for directories created with bad umask
-
-               if (r >= 0)
-                       continue;
-
-               struct stat st = {0};
-               r = stat(dirname, &st);
-               bool isdir = !!(st.st_mode & S_IFDIR);
-
-               if (!r && isdir)
-                       continue;
-               else if (!r && !isdir)
-                       errno = ENOTDIR;
-
-               _E("Failure while trying to ensure %s exists and it is directory: %m", dirname);
-               return false;
        }
 
        return true;
@@ -998,31 +963,6 @@ static bool execute_crash_modules(struct crash_info *cinfo)
        return true;
 }
 
-static int lock_dir(const char *path, bool block)
-{
-       assert(path);
-
-       int fd;
-
-       if ((fd = open(path, O_RDONLY | O_DIRECTORY)) == -1) {
-               _E("Failed to open %s: %m", path);
-               return -1;
-       }
-
-       int flock_flags = LOCK_EX;
-
-       if (!block)
-               flock_flags |= LOCK_NB;
-
-       if (flock(fd, flock_flags) == -1) {
-               _E("Failed to lock %s for exclusive access: %m", path);
-               close(fd);
-               return -1;
-       }
-
-       return fd;
-}
-
 static int mtime_cmp(const void *_a, const void *_b)
 {
        const struct file_info *a = _a;
index e26d261..59644b2 100644 (file)
@@ -19,6 +19,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <sys/file.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -197,18 +198,31 @@ int fsync_path(char *const path)
        return ret;
 }
 
-int make_dir(const char *path, const char *name, int mode)
+bool make_dir(const char *path, int mode)
 {
-       int r = -1;
+       int r = mkdir(path, mode);
 
-       DIR *dir = opendir(path);
-       if (dir) {
-               int dfd = dirfd(dir);
-               r = mkdirat(dfd, name, mode);
-               closedir(dir);
+       if (r < 0 && errno != EEXIST) {
+               _E("Unable to create directory %s: %m", path);
+               return false;
        }
 
-       return r == 0 || (r == -1 && errno == EEXIST) ? 0 : -1;
+       chmod(path, mode); // Fixup permissions for directories created with bad umask
+
+       if (r >= 0)
+               return true;
+
+       struct stat st = {0};
+       r = stat(path, &st);
+       bool isdir = !!(st.st_mode & S_IFDIR);
+
+       if (!r && isdir)
+               return true;
+       else if (!r && !isdir)
+               errno = ENOTDIR;
+
+       _E("Failure while trying to ensure %s exists and it is directory: %m", path);
+       return false;
 }
 
 static bool remove_dir_internal(int fd)
@@ -274,6 +288,38 @@ bool remove_dir(const char *path, bool del_dir)
        return ret;
 }
 
+int lock_dir(const char *path, bool block)
+{
+       assert(path);
+
+       int fd;
+
+       if ((fd = open(path, O_RDONLY | O_DIRECTORY)) == -1) {
+               _E("Failed to open %s: %m", path);
+               return -1;
+       }
+
+       int flock_flags = LOCK_EX;
+
+       if (!block)
+               flock_flags |= LOCK_NB;
+
+       if (flock(fd, flock_flags) == -1) {
+               _E("Failed to lock %s for exclusive access: %m", path);
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+void unlock_dir(int fd)
+{
+       if (flock(fd, LOCK_UN) < 0)
+               _E("Failed to unlock file descriptor: %m");
+       close(fd);
+}
+
 int get_exec_pid(const char *execpath)
 {
        DIR *dp;
index 85dd15f..32609e0 100644 (file)
@@ -46,10 +46,14 @@ int dump_file_write_fd(int dfd, char *src);
 
 int fsync_path(char *const path);
 
-int make_dir(const char *path, const char *name, int mode);
+bool make_dir(const char *path, int mode);
 
 bool remove_dir(const char *path, bool del_dir);
 
+int lock_dir(const char *path, bool block);
+
+void unlock_dir(int fd);
+
 int get_exec_pid(const char *execpath);
 
 int get_file_count(char *path);
index 47e1c23..8505fed 100644 (file)
@@ -32,7 +32,9 @@ configure_test("full_core")
 configure_test("info_file")
 configure_test("libbugreport-service")
 configure_test("libbugreport-service_systemstate")
+IF("${LIVEDUMPER}" STREQUAL "ON")
 configure_test("livedumper")
+ENDIF()
 configure_test("log_file")
 configure_test("output_param")
 configure_test("report_basic")