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)
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()
#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__ */
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
)
#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"
#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;
_D("Remove loop timeout");
}
-struct livedump_cb_data {
+struct callback_data {
int read_fd;
GDBusMethodInvocation *invocation;
GSource* source;
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)) {
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
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);
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);
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,
}
}
+ return;
+unsupported:
+ g_dbus_method_invocation_return_error(invocation,
+ CS_ERROR,
+ CS_ERR_UNSUPPORTED,
+ "Unsupported");
}
static GUnixFDList* prepare_unix_fd_list(int fd)
/* 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()) {
--- /dev/null
+/*
+ * 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, ¶m, 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 = ¶m1 };
+ (void)spawn_wait(args, NULL, ¶m0, 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;
+}
--- /dev/null
+/*
+ * 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
/* 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
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;
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;
#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>
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)
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;
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);
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")