summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>2019-07-25 11:02:43 +0200
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>2019-09-26 13:25:02 +0200
commit0d395b1e977387028e9515944b9c4e5731f52f19 (patch)
tree7a27312421487fece9c7d3208c48a4c054bdfdb3
parentaa2021fef99fb09e9b1080f613ad9b9d1e790d78 (diff)
downloadcrash-worker-0d395b1e977387028e9515944b9c4e5731f52f19.tar.gz
crash-worker-0d395b1e977387028e9515944b9c4e5731f52f19.tar.bz2
crash-worker-0d395b1e977387028e9515944b9c4e5731f52f19.zip
Add crash manager API
Privileged processes can send a D-Bus method call to create report of any living process. Signature is (iss): - INT (in) PID - STRING (in) dump reason - STRING (out) report path There is a library libcrashservice that sends the D-Bus method call. A function signature is: int livedump_pid(pid_t pid, const char *dump_reason, char *report_path); Change-Id: Id8528bdbaac517d4b5fc649821368e0ff020862f
-rw-r--r--CMakeLists.txt7
-rw-r--r--packaging/crash-worker.spec20
-rw-r--r--packaging/crash-worker_system-tests.spec5
-rw-r--r--src/crash-manager/crash-manager.c38
-rw-r--r--src/crash-manager/crash-manager.h1
-rw-r--r--src/crash-service/CMakeLists.txt60
-rw-r--r--src/crash-service/crash-service.c335
-rw-r--r--src/crash-service/crash-service.conf14
-rw-r--r--src/crash-service/crash-service.pc.in10
-rw-r--r--src/crash-service/crash-service.service.m410
-rw-r--r--src/crash-service/libcrash-service.c94
-rw-r--r--src/crash-service/libcrash-service.h30
-rw-r--r--src/crash-service/org.tizen.system.crash.livedump.service4
-rw-r--r--src/shared/util.c30
-rw-r--r--src/shared/util.h2
-rw-r--r--tests/system/CMakeLists.txt1
-rwxr-xr-xtests/system/libcrash-service/libcrash-service.sh.template47
-rw-r--r--tests/system/utils/CMakeLists.txt11
-rw-r--r--tests/system/utils/libcrash-servicetest.c47
19 files changed, 764 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0053f4c..df5c180 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,5 +26,12 @@ IF("${LIVEDUMPER}" STREQUAL "ON")
ADD_SUBDIRECTORY(src/livedumper)
ENDIF()
+IF("${CRASH_SERVICE}" STREQUAL "ON")
+ if (NOT "${LIVEDUMPER}" STREQUAL "ON")
+ message(FATAL_ERROR "Livedumper is required to build crash-service")
+ ENDIF()
+ ADD_SUBDIRECTORY(src/crash-service)
+ENDIF()
+
ADD_SUBDIRECTORY(tests)
diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec
index a597a4b..015bb07 100644
--- a/packaging/crash-worker.spec
+++ b/packaging/crash-worker.spec
@@ -4,11 +4,13 @@
%define _with_tests on
%define _with_logdump on
%define _with_livedumper on
+%define _with_crashservice on
%bcond_with doc
%bcond_with sys_assert
%bcond_with tests
%bcond_with logdump
%bcond_with livedumper
+%bcond_with crashservice
# NOTE: To disable coredump set DumpCore=0 in configuration file
@@ -55,6 +57,7 @@ Requires: %{_bindir}/buxton2ctl
crash-manager
%package devel
+Requires: crash-worker
Summary: Crash-manager development package
%description devel
This package provides library and header files.
@@ -140,6 +143,7 @@ export CFLAGS+=" -Werror"
-DSYS_ASSERT=%{on_off sys_assert} \
-DLOG_DUMP=%{on_off logdump} \
-DLIVEDUMPER=%{on_off livedumper} \
+ -DCRASH_SERVICE=%{on_off crashservice} \
-DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \
-DLOGGER=dlog \
-DVERSION=%{version}
@@ -215,6 +219,14 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload
%{_libexecdir}/crash-notify-send
%{_libdir}/libcrash-manager.so.*
+%if %{with crashservice}
+%attr(0750,system_fw,system_fw) %{_bindir}/crash-service
+%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
+%{_libdir}/libcrash-service.so.*
+%endif
+
%if %{with logdump}
%dir %{crash_all_log}
%{crash_dump_gen}/*
@@ -235,7 +247,12 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload
%files devel
%{_includedir}/crash-manager.h
%{_libdir}/libcrash-manager.so
-%{_datadir}/pkgconfig/*.pc
+%{_datadir}/pkgconfig/crash-manager.pc
+%if %{with crashservice}
+%{_includedir}/libcrash-service.h
+%{_libdir}/libcrash-service.so
+%{_datadir}/pkgconfig/crash-service.pc
+%endif
%if %{with doc}
%files doc
@@ -263,4 +280,3 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload
%manifest %{name}.manifest
%{_bindir}/livedumper
%endif
-
diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec
index 3fea883..320c900 100644
--- a/packaging/crash-worker_system-tests.spec
+++ b/packaging/crash-worker_system-tests.spec
@@ -16,6 +16,9 @@ Source0: %{name}-%{version}.tar.gz
Source1001: crash-worker_system-tests.manifest
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(rpm)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(crash-service)
BuildRequires: cmake
Requires: diff
@@ -75,10 +78,12 @@ cd tests/system
%{_libdir}/crash-worker_system-tests/time_test/time_test.sh
%{_libdir}/crash-worker_system-tests/utils/btee
%{_libdir}/crash-worker_system-tests/utils/kenny
+%{_libdir}/crash-worker_system-tests/utils/libcrash-servicetest
%{_libdir}/crash-worker_system-tests/utils/minicore-utils.sh
%{_libdir}/crash-worker_system-tests/wait_for_opt_usr/wait_for_opt_usr.sh
%{_libdir}/crash-worker_system-tests/without_core/without_core.sh
%{_libdir}/crash-worker_system-tests/output_param/output_param.sh
+%{_libdir}/crash-worker_system-tests/libcrash-service/libcrash-service.sh
%if %{with livedumper}
%{_libdir}/crash-worker_system-tests/livedumper/livedumper.sh
%endif
diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c
index be79b5b..dd2bab1 100644
--- a/src/crash-manager/crash-manager.c
+++ b/src/crash-manager/crash-manager.c
@@ -1216,6 +1216,18 @@ static bool crash_manager_prepare(struct crash_info *cinfo)
return true;
}
+static void write_dump_reason(const char *reason, const char *base_dir, const char *name)
+{
+ char *reason_name;
+
+ if (asprintf(&reason_name, "%s.dump_reason", name) == -1) {
+ _E("Failed to asprintf for reason_name: %m");
+ } else {
+ write_to_file(reason, base_dir, reason_name);
+ free(reason_name);
+ }
+}
+
void crash_manager_free(struct crash_info *cinfo)
{
if (cinfo->prstatus_fd >= 0)
@@ -1235,3 +1247,29 @@ bool crash_manager_direct(struct crash_info *cinfo)
return run(cinfo);
}
+bool crash_manager_livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len)
+{
+ bool result = false;
+ struct crash_info cinfo;
+ crash_info_init(&cinfo);
+
+ cinfo.livedump = true;
+ cinfo.pid_info = pid;
+
+ if (!crash_manager_prepare(&cinfo))
+ goto exit;
+
+ write_dump_reason(dump_reason, cinfo.pfx, cinfo.name);
+
+ if (!run(&cinfo))
+ goto exit;
+
+ strncpy(report_path, cinfo.result_path, report_path_len);
+
+ result = true;
+exit:
+ crash_manager_free(&cinfo);
+ _I("Exiting with %s", result ? "success" : "fail");
+ return result;
+}
+
diff --git a/src/crash-manager/crash-manager.h b/src/crash-manager/crash-manager.h
index 244b37a..7c102bc 100644
--- a/src/crash-manager/crash-manager.h
+++ b/src/crash-manager/crash-manager.h
@@ -57,6 +57,7 @@ struct crash_info {
};
bool crash_manager_direct(struct crash_info *cinfo);
+bool crash_manager_livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len);
void crash_info_init(struct crash_info *cinfo);
void crash_manager_free(struct crash_info *cinfo);
#endif
diff --git a/src/crash-service/CMakeLists.txt b/src/crash-service/CMakeLists.txt
new file mode 100644
index 0000000..37ad6b1
--- /dev/null
+++ b/src/crash-service/CMakeLists.txt
@@ -0,0 +1,60 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(crash-service C)
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/)
+
+SET(CRASH_SERVICE_SRCS
+ crash-service.c
+ )
+
+INCLUDE(FindPkgConfig)
+
+pkg_check_modules(crash-service_pkgs REQUIRED
+ dlog
+ gio-2.0
+ )
+
+FOREACH(flag ${crash-service_pkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable")
+
+INCLUDE(${CMAKE_SOURCE_DIR}/cmake/ProcessM4.cmake)
+
+LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/crash-manager)
+ADD_EXECUTABLE(${PROJECT_NAME} ${CRASH_SERVICE_SRCS})
+ADD_DEPENDENCIES(${PROJECT_NAME} crash-manager)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-service_pkgs_LDFLAGS} -pie -lrt -lcrash-manager)
+
+ADD_LIBRARY(libcrash-service SHARED libcrash-service.c)
+SET_TARGET_PROPERTIES(libcrash-service PROPERTIES
+ SOVERSION 1
+ PUBLIC_HEADER libcrash-service.h
+ OUTPUT_NAME crash-service)
+
+PROCESS_M4("${M4_DEFINES}"
+ "${CMAKE_CURRENT_SOURCE_DIR}/crash-service.service.m4"
+ "${CMAKE_CURRENT_SOURCE_DIR}/crash-service.service")
+
+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}/crash-service.conf
+ DESTINATION /etc/dbus-1/system.d)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.service
+ DESTINATION /usr/lib/systemd/system
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
+
+INSTALL(TARGETS libcrash-service LIBRARY DESTINATION /usr/lib/
+ PUBLIC_HEADER DESTINATION /usr/include)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.pc
+ DESTINATION share/pkgconfig)
diff --git a/src/crash-service/crash-service.c b/src/crash-service/crash-service.c
new file mode 100644
index 0000000..fa223f2
--- /dev/null
+++ b/src/crash-service/crash-service.c
@@ -0,0 +1,335 @@
+/*
+ * crash-service
+ *
+ * Copyright (c) 2019 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 <gio/gio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "crash-manager/crash-manager.h"
+#include "libcrash-service.h"
+#include "shared/log.h"
+
+/* Dbus activation */
+#define CRASH_BUS_NAME "org.tizen.system.crash.livedump"
+#define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash/Livedump"
+
+#define TIMEOUT_INTERVAL_SEC 60
+#define TIMEOUT_LIVEDUMP_SEC 50
+
+#define CS_ERROR 1
+#define CS_ERR_PARAM 1
+#define CS_ERR_TIMEOUT 2
+#define CS_ERR_READ 3
+#define CS_ERR_INTERNAL 4
+
+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.crash.livedump'>"
+" <method name='livedump_pid'>"
+" <arg type='i' name='pid' direction='in'/>"
+" <arg type='s' name='dump_reason' direction='in'/>"
+" <arg type='s' name='report_path' direction='out'/>"
+" </method>"
+" </interface>"
+"</node>";
+
+void child_exit(int sig)
+{
+ wait(NULL);
+}
+
+static int timeout_cb(gpointer data)
+{
+ _I("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);
+ _D("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);
+ _D("Remove loop timeout");
+}
+
+struct livedump_cb_data {
+ int read_fd;
+ GDBusMethodInvocation *invocation;
+ GSource* source;
+ pid_t child_pid;
+};
+
+static bool data_ready(int fd)
+{
+ fd_set rfd;
+ FD_ZERO(&rfd);
+ FD_SET(fd, &rfd);
+ struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
+ int retval = select(fd+1, &rfd, NULL, NULL, &tv);
+ if (retval == -1)
+ _E("select() error: %m");
+ return retval == 1;
+}
+
+static gboolean read_result_cb(gpointer data)
+{
+ struct livedump_cb_data *cb_data = (struct livedump_cb_data*)data;
+ char report_path[PATH_MAX];
+
+ if (!data_ready(cb_data->read_fd)) {
+ _I("Report is not ready after %d seconds.", TIMEOUT_LIVEDUMP_SEC);
+ g_dbus_method_invocation_return_error(cb_data->invocation,
+ CS_ERROR,
+ CS_ERR_TIMEOUT,
+ "Report is not ready");
+ kill(cb_data->child_pid, SIGKILL);
+ goto end;
+ } else {
+ if (read(cb_data->read_fd, report_path, sizeof(report_path)) == -1) {
+ _E("Read from child error: %m");
+ g_dbus_method_invocation_return_error(cb_data->invocation,
+ CS_ERROR,
+ CS_ERR_READ,
+ "Error while obtaining report path");
+ goto end;
+ }
+ }
+
+ g_dbus_method_invocation_return_value(cb_data->invocation,
+ g_variant_new("(s)", report_path));
+end:
+ close(cb_data->read_fd);
+ free(cb_data);
+ return G_SOURCE_REMOVE;
+}
+
+static bool livedump_run(int write_fd, pid_t pid, const gchar *dump_reason)
+{
+ char report_path[PATH_MAX];
+
+ if (!crash_manager_livedump_pid(pid, 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) {
+ _E("Write report_path error: %m");
+ close(write_fd);
+ return false;
+ }
+
+ close(write_fd);
+ return true;
+}
+
+static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, gchar *dump_reason)
+{
+ /*
+ * We want to run the livedump in different process so as not to
+ * block the main loop, to be able to handle many method calls
+ * in the same time, so we need a communication cannel to send
+ * back a result report path.
+ */
+ int fds[2];
+ if (pipe(fds) == -1) {
+ _E("Pipe error: %m");
+ g_dbus_method_invocation_return_error(invocation,
+ CS_ERROR,
+ CS_ERR_INTERNAL,
+ "Internal error");
+ return;
+ }
+
+ struct livedump_cb_data *cb_data = (struct livedump_cb_data*)malloc(sizeof(struct livedump_cb_data));
+ if (cb_data == NULL) {
+ _E("cb_data malloc() error: %m");
+ exit(EXIT_FAILURE);
+ }
+ cb_data->read_fd = fds[0];
+ cb_data->invocation = invocation;
+
+ GSource *source = g_timeout_source_new_seconds(TIMEOUT_LIVEDUMP_SEC);
+ cb_data->source = source;
+ g_source_add_unix_fd(source, fds[0], G_IO_IN);
+ g_source_set_callback(source, read_result_cb, cb_data, NULL);
+ g_source_attach(source, NULL);
+
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ close(fds[0]);
+ if (livedump_run(fds[1], pid, dump_reason))
+ exit(EXIT_SUCCESS);
+ else
+ exit(EXIT_FAILURE);
+ } else if (child_pid > 0) {
+ cb_data->child_pid = child_pid;
+ close(fds[1]);
+ } else {
+ _E("fork() error: %m");
+ g_dbus_method_invocation_return_error(invocation,
+ CS_ERROR,
+ CS_ERR_INTERNAL,
+ "Internal error");
+ close(fds[0]);
+ close(fds[1]);
+ }
+}
+
+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, "livedump_pid") == 0) {
+ gchar *dump_reason;
+ const 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);
+ } else {
+ _E("Parameters are not of the correct type (is)");
+ g_dbus_method_invocation_return_error(invocation,
+ CS_ERROR,
+ CS_ERR_PARAM,
+ "Parameters are not of the correct type (is)");
+ }
+ }
+
+ 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)
+{
+ guint registration_id;
+
+ GError *error = NULL;
+ registration_id = g_dbus_connection_register_object(conn,
+ CRASH_OBJECT_PATH,
+ introspection_data->interfaces[0],
+ &interface_vtable, NULL, NULL, &error);
+ if (registration_id == 0 || error) {
+ _E("Failed to g_dbus_connection_register_object: %s", error ? error->message : "");
+ if (error)
+ g_error_free(error);
+ }
+}
+
+static void on_name_acquired(GDBusConnection *conn,
+ const gchar *name,
+ gpointer user_data)
+{
+ _D("Acquired the name %s on the system bus", name);
+}
+
+static void on_name_lost(GDBusConnection *conn,
+ const gchar *name,
+ gpointer user_data)
+{
+ _D("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) {
+ _E("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) {
+ _E("Failed to get dbus: %s", error ? error->message : "");
+ if (error)
+ g_error_free(error);
+ return false;
+ }
+
+ g_bus_own_name(G_BUS_TYPE_SYSTEM, CRASH_BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
+ on_name_acquired, on_name_lost, NULL, NULL);
+
+ return true;
+}
+
+int main(void)
+{
+ signal(SIGCHLD, child_exit);
+ loop = g_main_loop_new(NULL, false);
+
+ 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/crash-service/crash-service.conf b/src/crash-service/crash-service.conf
new file mode 100644
index 0000000..904d93a
--- /dev/null
+++ b/src/crash-service/crash-service.conf
@@ -0,0 +1,14 @@
+<!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.crash.livedump"/>
+ <allow send_destination="org.tizen.system.crash.livedump"
+ send_interface="org.tizen.system.crash.livedump"
+ send_member="livedump_pid"/>
+ </policy>
+ <policy context="default">
+ <deny own="org.tizen.system.crash.livedump"/>
+ <deny send_destination="org.tizen.system.crash.livedump"/>
+ </policy>
+</busconfig>
diff --git a/src/crash-service/crash-service.pc.in b/src/crash-service/crash-service.pc.in
new file mode 100644
index 0000000..9d2d7c8
--- /dev/null
+++ b/src/crash-service/crash-service.pc.in
@@ -0,0 +1,10 @@
+prefix=/usr
+exec_prefix=${prefix}
+includedir=${prefix}/include
+libdir=${exec_prefix}/lib
+
+Name: crash-service
+Description: The crash-service library
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lcrash-service
diff --git a/src/crash-service/crash-service.service.m4 b/src/crash-service/crash-service.service.m4
new file mode 100644
index 0000000..f080f40
--- /dev/null
+++ b/src/crash-service/crash-service.service.m4
@@ -0,0 +1,10 @@
+[Unit]
+Description=crash service
+
+[Service]
+Type=dbus
+BusName=org.tizen.system.crash.livedump
+ExecStart=/usr/bin/crash-service
+SmackProcessLabel=System
+Nice=-5
+KillMode=mixed
diff --git a/src/crash-service/libcrash-service.c b/src/crash-service/libcrash-service.c
new file mode 100644
index 0000000..25a36bc
--- /dev/null
+++ b/src/crash-service/libcrash-service.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016-2019 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 <gio/gio.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "LIBCRASH-SERVICE"
+
+#include "shared/log.h"
+
+#define BUS_NAME "org.tizen.system.crash.livedump"
+#define OBJECT_PATH "/Org/Tizen/System/Crash/Livedump"
+#define INTERFACE_NAME "org.tizen.system.crash.livedump"
+#define METHOD_NAME "livedump_pid"
+
+static GDBusConnection* dbus_init(void)
+{
+ GError *error = NULL;
+ GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+
+ if (!conn || error) {
+ _E("Failed to get dbus: %s", error ? error->message : "");
+ if (error)
+ g_error_free(error);
+ return NULL;
+ }
+
+ return conn;
+}
+
+bool livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len)
+{
+ bool res = true;
+ GDBusConnection *conn = dbus_init();
+
+ if (!conn)
+ return false;
+
+ GVariant *parameters = g_variant_new("(is)", pid, dump_reason);
+
+ GError *error = NULL;
+ GVariant *reply = g_dbus_connection_call_sync(conn,
+ BUS_NAME,
+ OBJECT_PATH,
+ INTERFACE_NAME,
+ METHOD_NAME,
+ parameters,
+ G_VARIANT_TYPE("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (!reply || error) {
+ _E("Error while calling livedump_pid via dbus (pid=%d, reason=%s): %s", pid, dump_reason, error ? error->message : "");
+ if (error)
+ g_error_free(error);
+ res = false;
+ goto exit;
+ }
+
+ if (!g_variant_is_of_type(reply, G_VARIANT_TYPE("(s)"))) {
+ _E("reply is not of the correct type (s)");
+ res = false;
+ goto exit;
+ }
+
+ gchar *reply_str;
+ g_variant_get(reply, "(&s)", &reply_str);
+ if (strlen(reply_str) <= (report_path_len + 1)) {
+ strncpy(report_path, reply_str, report_path_len);
+ } else {
+ _E("Report path (%s) is longer than report_path_len", reply_str);
+ res = false;
+ }
+exit:
+ g_object_unref(conn);
+ return res;
+}
diff --git a/src/crash-service/libcrash-service.h b/src/crash-service/libcrash-service.h
new file mode 100644
index 0000000..a2fa39c
--- /dev/null
+++ b/src/crash-service/libcrash-service.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 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 LIBCRASH_SERVICE_H
+#define LIBCRASH_SERVICE_H
+#include <stdbool.h>
+#include <sys/types.h>
+
+/**
+ * @brief Sends a request to the crash-service to create livedump report of specific process.
+ * @param pid PID of process for which the report should be created.
+ * @param dump_reason the reason that should be included in the raport.
+ * @param report_path pointer to the buffer in which will be saved the report path.
+ * @param report_path_len lenght of buffer for the report path.
+ * @return true on success and false on error.
+ */
+extern bool livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len);
+#endif /* LIBCRASH_SERVICE_H */
diff --git a/src/crash-service/org.tizen.system.crash.livedump.service b/src/crash-service/org.tizen.system.crash.livedump.service
new file mode 100644
index 0000000..f1fc1fd
--- /dev/null
+++ b/src/crash-service/org.tizen.system.crash.livedump.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.tizen.system.crash.livedump
+Exec=/bin/false
+SystemdService=crash-service.service
diff --git a/src/shared/util.c b/src/shared/util.c
index 1860bab..d5917e7 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -584,6 +584,36 @@ bool file_exists(const char *path)
return stat(path, &buf) == 0;
}
+bool write_to_file(const char *content, const char *base_dir, const char *file_name)
+{
+ char *path;
+ bool result = false;
+
+ if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
+ _E("Failed to asprintf for path: %m");
+ return false;
+ }
+
+ int fd = open(path, O_WRONLY | O_CREAT, 0600);
+
+ if (fd < 0) {
+ _E("Failed to open %s: %m", path);
+ goto exit;
+ }
+
+ if (dprintf(fd, "%s", content) < 0) {
+ _E("Failed to write to file %s: %m", path);
+ close(fd);
+ goto exit;
+ }
+
+ close(fd);
+ result = true;
+exit:
+ free(path);
+ return result;
+}
+
/**
* @}
*/
diff --git a/src/shared/util.h b/src/shared/util.h
index f3177e8..8e2a82f 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -70,6 +70,8 @@ bool string_ends_with(const char *string, const char *suffix);
bool file_exists(const char *path);
+bool write_to_file(const char *content, const char *base_dir, const char *file_name);
+
#ifdef __cplusplus
}
#endif
diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt
index 309e572..92796b6 100644
--- a/tests/system/CMakeLists.txt
+++ b/tests/system/CMakeLists.txt
@@ -40,6 +40,7 @@ configure_test("livedumper")
configure_test("extra_script")
configure_test("dbus_notify")
configure_test("output_param")
+configure_test("libcrash-service")
get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST)
diff --git a/tests/system/libcrash-service/libcrash-service.sh.template b/tests/system/libcrash-service/libcrash-service.sh.template
new file mode 100755
index 0000000..611978a
--- /dev/null
+++ b/tests/system/libcrash-service/libcrash-service.sh.template
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Test --output parameter
+
+if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then
+ CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@"
+fi
+
+. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh
+
+save_core_pattern
+trap restore_core_pattern 0
+
+echo "|/usr/bin/crash-manager -p %p -u %u -g %g -s %s -t %t" > /proc/sys/kernel/core_pattern
+
+{
+ ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny 10 &
+ KENNY_PID=$!
+ sleep 1
+} 1> /dev/null 2>&1
+
+sleep 2
+
+rm -rf ${LIVE_DUMP_PATH}/*
+REASON="some reason"
+${CRASH_WORKER_SYSTEM_TESTS}/utils/libcrash-servicetest -r "${REASON}" ${KENNY_PID}
+
+wait_for_file ${LIVE_DUMP_PATH}/kenny*zip
+
+kill -9 ${KENNY_PID}
+
+trap popd 0
+
+pushd ${LIVE_DUMP_PATH}
+
+unzip kenny*zip
+cd kenny*
+
+if [ ! -f *dump_reason ]; then
+ fail "dump_reason file doesn't exist"
+fi
+
+if [ "$(cat *dump_reason)" != "${REASON}" ]; then
+ fail "Dump reason didn't match"
+fi
+
+exit_ok
diff --git a/tests/system/utils/CMakeLists.txt b/tests/system/utils/CMakeLists.txt
index a486aab..5bd6710 100644
--- a/tests/system/utils/CMakeLists.txt
+++ b/tests/system/utils/CMakeLists.txt
@@ -8,7 +8,18 @@ find_package(Threads)
target_link_libraries(kenny ${CMAKE_THREAD_LIBS_INIT})
set_target_properties(kenny PROPERTIES COMPILE_FLAGS "-std=c++11 -ggdb -O0")
+add_executable(libcrash-servicetest libcrash-servicetest.c)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(helper_pkgs REQUIRED
+ crash-service
+ gio-2.0
+ dlog)
+
+TARGET_LINK_LIBRARIES(libcrash-servicetest crash-service ${helper_pkgs_LDFLAGS})
+
install(TARGETS kenny DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils)
install(TARGETS btee DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils)
install(FILES minicore-utils.sh DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils)
+install(TARGETS libcrash-servicetest DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils)
diff --git a/tests/system/utils/libcrash-servicetest.c b/tests/system/utils/libcrash-servicetest.c
new file mode 100644
index 0000000..c83a2fc
--- /dev/null
+++ b/tests/system/utils/libcrash-servicetest.c
@@ -0,0 +1,47 @@
+#include <libcrash-service.h>
+
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void help(char *argv_0)
+{
+ printf("Usage: %s [-r dump_reason] pid\n", argv_0);
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ const char *dump_reason = NULL;
+
+ while ((opt = getopt(argc, argv, "r:")) != -1) {
+ switch (opt) {
+ case 'r':
+ dump_reason = optarg;
+ break;
+ default:
+ help(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (dump_reason == NULL)
+ dump_reason = "no reason";
+
+ if (optind >= argc) {
+ help(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ pid_t pid = strtol(argv[optind], NULL, 10);
+ if (pid == 0) {
+ printf("ERROR: pid must be a number\n");
+ help(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ char BUFF[PATH_MAX];
+ bool res = livedump_pid(pid, dump_reason, BUFF, PATH_MAX);
+ printf("res: %s\nreport_path: %s\n", res ? "true" : "false", BUFF);
+ return res ? EXIT_SUCCESS : EXIT_FAILURE;
+}