--- /dev/null
+/*
+ * crash-manager
+ *
+ * Copyright (c) 2016 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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/smack.h>
+#include <gio/gio.h>
+#include <tzplatform_config.h>
+#include <dlog.h>
+#include "crash-manager.h"
+#include "shared/util.h"
+
+#undef LOG_TAG
+#define LOG_TAG "CRASH_MANAGER"
+
+#define DEBUGMODE_FILE tzplatform_mkpath(TZ_SYS_ETC, ".debugmode")
+
+/* Crash-popup dbus */
+#define POPUP_BUS_NAME "org.tizen.system.popup"
+#define POPUP_OBJECT_PATH "/Org/Tizen/System/Popup/Crash"
+#define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash"
+#define POPUP_METHOD "PopupLaunch"
+
+/* Paths and variables */
+static struct crash_info {
+ char *cmd_info;
+ char *pid_info;
+ char time_info[80];
+ char temp_dir[PATH_MAX];
+ char name[FILENAME_MAX];
+ char result_path[PATH_MAX];
+ char pfx[PATH_MAX];
+ char info_path[PATH_MAX];
+ char core_path[PATH_MAX];
+ char log_path[PATH_MAX];
+#ifdef SYS_ASSERT
+ char sysassert_cs_path[PATH_MAX];
+#endif
+} crash_info;
+
+static int make_dump_dir(void)
+{
+ struct stat st;
+
+ if (!stat(CRASH_PATH, &st)) {
+ if (!(st.st_mode & S_IFDIR)) {
+ LOGE("%s (not DIR) is already exist", CRASH_PATH);
+ return -1;
+ }
+ } else {
+ if (mkdir(CRASH_PATH, 0775) < 0) {
+ LOGE("Failed to mkdir for %s", CRASH_PATH);
+ return -1;
+ }
+ smack_setlabel(CRASH_PATH, "System::Shared",
+ SMACK_LABEL_ACCESS);
+ smack_setlabel(CRASH_PATH, "1", SMACK_LABEL_TRANSMUTE);
+ }
+
+ if (!stat(CRASH_TEMP, &st)) {
+ if (!(st.st_mode & S_IFDIR)) {
+ LOGE("%s (not DIR) is already exist", CRASH_TEMP);
+ return -1;
+ }
+ } else {
+ if (mkdir(CRASH_TEMP, 0775) < 0) {
+ LOGE("Failed to mkdir for %s", CRASH_TEMP);
+ return -1;
+ }
+ smack_setlabel(CRASH_TEMP, "System::Shared",
+ SMACK_LABEL_ACCESS);
+ smack_setlabel(CRASH_TEMP, "1", SMACK_LABEL_TRANSMUTE);
+ }
+
+ return 0;
+}
+
+static int set_crash_info(char *argv[])
+{
+ int ret;
+ char *temp_dir_ret;
+ time_t time_val;
+ struct tm loc_tm;
+
+ crash_info.cmd_info = argv[6];
+ crash_info.pid_info = argv[1];
+
+ time_val = atoll(argv[5]);
+ localtime_r(&time_val, &loc_tm);
+ strftime(crash_info.time_info, sizeof(crash_info.time_info),
+ "%Y%m%d%H%M%S", &loc_tm);
+
+ ret = snprintf(crash_info.temp_dir, sizeof(crash_info.temp_dir),
+ "%s/crash.XXXXXX", CRASH_TEMP);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for temp_dir");
+ return -1;
+ }
+ temp_dir_ret = mkdtemp(crash_info.temp_dir);
+ if (access(temp_dir_ret, F_OK)) {
+ LOGE("Failed to mkdtemp for temp_dir");
+ return -1;
+ }
+
+ ret = snprintf(crash_info.name, sizeof(crash_info.name), "%s_%s_%s",
+ crash_info.cmd_info,
+ crash_info.pid_info,
+ crash_info.time_info);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for name");
+ goto rm_temp;
+ }
+
+ ret = snprintf(crash_info.result_path, sizeof(crash_info.result_path),
+ "%s/%s.tar.gz", CRASH_PATH, crash_info.name);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for result path");
+ goto rm_temp;
+ }
+
+ ret = snprintf(crash_info.pfx, sizeof(crash_info.pfx), "%s/%s",
+ crash_info.temp_dir, crash_info.name);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for pfx");
+ goto rm_temp;
+ }
+ ret = mkdir(crash_info.pfx, 0775);
+ if (ret < 0) {
+ LOGE("Failed to mkdir for %s", crash_info.pfx);
+ goto rm_temp;
+ }
+
+ ret = snprintf(crash_info.info_path, sizeof(crash_info.info_path),
+ "%s/%s.info", crash_info.pfx, crash_info.name);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for info path");
+ goto rm_temp;
+ }
+
+ ret = snprintf(crash_info.core_path, sizeof(crash_info.core_path),
+ "%s/%s.coredump", crash_info.pfx, crash_info.name);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for core path");
+ goto rm_temp;
+ }
+
+ ret = snprintf(crash_info.log_path, sizeof(crash_info.log_path),
+ "%s/%s.log", crash_info.pfx, crash_info.name);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for log path");
+ goto rm_temp;
+ }
+
+#ifdef SYS_ASSERT
+ ret = snprintf(crash_info.sysassert_cs_path,
+ sizeof(crash_info.sysassert_cs_path),
+ "/tmp/crash_stack/%s_%s.info",
+ crash_info.cmd_info, crash_info.pid_info);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for sys-assert callstack path");
+ goto rm_temp;
+ }
+#endif
+
+ return 0;
+
+rm_temp:
+ remove_dir(crash_info.temp_dir, 1);
+ return -1;
+}
+
+#ifdef SYS_ASSERT
+static int get_sysassert_cs(void)
+{
+ int ret;
+ char move_path[PATH_MAX];
+
+ if (access(crash_info.sysassert_cs_path, F_OK)) {
+ LOGE("The sys-assert cs file not found: %s",
+ crash_info.sysassert_cs_path);
+ return -1;
+ }
+
+ ret = snprintf(move_path, sizeof(move_path), "%s/%s",
+ crash_info.pfx, basename(crash_info.sysassert_cs_path));
+ if (ret < 0) {
+ LOGE("Failed to snprintf for move path");
+ return -1;
+ }
+
+ if (move_file(crash_info.sysassert_cs_path, move_path) < 0) {
+ LOGE("Failed to move %s to %s",
+ crash_info.sysassert_cs_path, move_path);
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int convert_path(char *path, char *core_exepath)
+{
+ int i;
+
+ for (i = 0; core_exepath[i]; i++)
+ path[i] = (core_exepath[i] == '!' ? '/' : core_exepath[i]);
+ path[i] = '\0';
+
+ return i;
+}
+
+static void launch_crash_popup(char *core_exepath)
+{
+ GDBusConnection *conn;
+ GVariantBuilder *builder;
+ GVariant *parameters = NULL;
+ GVariant *reply = NULL;
+ GError *error = NULL;
+ int ret;
+ char exepath[PATH_MAX] = "\0";
+
+ if (convert_path(exepath, core_exepath) <= 0) {
+ LOGE("Failed to parsing exepath");
+ return;
+ }
+
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (error) {
+ LOGE("Failed to get dbus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}"));
+ g_variant_builder_add(builder, "{ss}", "_SYSPOPUP_CONTENT_", "crash");
+ g_variant_builder_add(builder, "{ss}", "_PROCESS_NAME_",
+ crash_info.cmd_info);
+ g_variant_builder_add(builder, "{ss}", "_EXEPATH_", exepath);
+ parameters = g_variant_new("(a{ss})", builder);
+ g_variant_builder_unref(builder);
+
+ reply = g_dbus_connection_call_sync(conn,
+ POPUP_BUS_NAME,
+ POPUP_OBJECT_PATH,
+ POPUP_INTERFACE_NAME,
+ POPUP_METHOD,
+ parameters,
+ G_VARIANT_TYPE("(i)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ 120000,
+ NULL,
+ &error);
+ if (error) {
+ LOGE("Failed to get reply: %s", error->message);
+ g_error_free(error);
+ goto exit;
+ }
+
+ g_variant_get(reply, "(i)", &ret);
+ LOGI("Crash_popup is launched: (%d)", ret);
+
+exit:
+ if (reply)
+ g_variant_unref(reply);
+ if (parameters)
+ g_variant_unref(parameters);
+}
+
+static void dump_system_state(void)
+{
+ int ret;
+ char command[PATH_MAX];
+
+ ret = snprintf(command, sizeof(command),
+ "/usr/bin/dump_systemstate -d -k -f %s",
+ crash_info.log_path);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for dump_systemstate command");
+ return;
+ }
+ system_command(command);
+}
+
+static void execute_crash_modules(int argc, char *argv[], int debug)
+{
+ int ret, i;
+ char arg_append[PATH_MAX];
+ char command[PATH_MAX];
+
+ arg_append[0] = '\0';
+ for (i = 1; i < argc; i++) {
+ strcat(arg_append, argv[i]);
+ strcat(arg_append, " ");
+ }
+
+ /* Execute crash-pipe */
+ if (debug)
+ ret = snprintf(command, sizeof(command),
+ "%s --save-core %s --report %s > %s",
+ CRASH_PIPE_PATH,
+ crash_info.core_path, arg_append,
+ crash_info.info_path);
+ else
+ ret = snprintf(command, sizeof(command),
+ "%s --report %s > %s",
+ CRASH_PIPE_PATH,
+ arg_append,
+ crash_info.info_path);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for crash-pipe command");
+ return;
+ }
+ system_command(command);
+
+ /* Execute crash-stack */
+ /*
+ ret = snprintf(command, sizeof(command),
+ "%s --pid %s >> %s",
+ CRASH_STACK_PATH,
+ crash_info.pid_info, crash_info.info_path);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for crash-stack command");
+ return;
+ }
+ system_command(command);
+ */
+}
+
+static void compress(void)
+{
+ int ret;
+ char tar_path[PATH_MAX];
+ char command[PATH_MAX];
+
+ ret = snprintf(tar_path, sizeof(tar_path), "%s/report.tar.gz",
+ crash_info.temp_dir);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for tar path");
+ return;
+ }
+
+ ret = snprintf(command, sizeof(command),
+ "/bin/tar -czf %s -C %s %s",
+ tar_path, crash_info.temp_dir, crash_info.name);
+ if (ret < 0) {
+ LOGE("Failed to snprintf for tar command");
+ return;
+ }
+ system_command(command);
+
+ if (move_file(tar_path, crash_info.result_path) < 0)
+ LOGE("Failed to move %s to %s",
+ tar_path, crash_info.result_path);
+
+ ret = remove_dir(crash_info.temp_dir, 1);
+ if (ret < 0)
+ LOGE("Failed to delete temp directory");
+}
+
+int main(int argc, char *argv[])
+{
+ prctl(PR_SET_DUMPABLE, 0);
+
+ /* Create crash directories */
+ if (make_dump_dir() < 0)
+ exit(EXIT_FAILURE);
+
+ /* Set crash info */
+ if (set_crash_info(argv) < 0)
+ exit(EXIT_FAILURE);
+
+#ifdef SYS_ASSERT
+ /* Fetch callstack of sys-assert */
+ get_sysassert_cs();
+#endif
+
+ /* .dbugmode: launch crash-popup */
+ if (access(DEBUGMODE_FILE, F_OK) == 0)
+ launch_crash_popup(argv[7]);
+
+ /* Exec dump_systemstate */
+ dump_system_state();
+
+ /* Exec crash modules */
+ execute_crash_modules(argc, argv, DEBUG);
+
+ /* Tar compression */
+ compress();
+
+ return 0;
+}