4 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
22 #include <sys/select.h>
23 #include <sys/types.h>
27 #include "crash-manager/crash-manager.h"
28 #include "libcrash-service.h"
29 #include "shared/log.h"
32 #define CRASH_BUS_NAME "org.tizen.system.crash.livedump"
33 #define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash/Livedump"
35 #define TIMEOUT_INTERVAL_SEC 60
36 #define TIMEOUT_LIVEDUMP_SEC 50
39 #define CS_ERR_PARAM 1
40 #define CS_ERR_TIMEOUT 2
42 #define CS_ERR_INTERNAL 4
44 static GMainLoop *loop;
45 static GMutex timeout_mutex;
46 static guint timeout_id;
47 static GDBusNodeInfo *introspection_data;
48 static const gchar introspection_xml[] =
50 " <interface name='org.tizen.system.crash.livedump'>"
51 " <method name='livedump_pid'>"
52 " <arg type='i' name='pid' direction='in'/>"
53 " <arg type='s' name='dump_reason' direction='in'/>"
54 " <arg type='s' name='report_path' direction='out'/>"
59 void child_exit(int sig)
64 static int timeout_cb(gpointer data)
67 g_main_loop_quit((GMainLoop *)data);
72 static void add_timeout(void)
74 g_mutex_lock(&timeout_mutex);
77 g_source_remove(timeout_id);
78 timeout_id = g_timeout_add_seconds(TIMEOUT_INTERVAL_SEC, timeout_cb, loop);
80 g_mutex_unlock(&timeout_mutex);
81 _D("Add loop timeout (%d)", TIMEOUT_INTERVAL_SEC);
84 static void remove_timeout(void)
86 g_mutex_lock(&timeout_mutex);
89 g_source_remove(timeout_id);
93 g_mutex_unlock(&timeout_mutex);
94 _D("Remove loop timeout");
97 struct livedump_cb_data {
99 GDBusMethodInvocation *invocation;
104 static bool data_ready(int fd)
109 struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
110 int retval = select(fd+1, &rfd, NULL, NULL, &tv);
112 _E("select() error: %m");
116 static gboolean read_result_cb(gpointer data)
118 struct livedump_cb_data *cb_data = (struct livedump_cb_data*)data;
119 char report_path[PATH_MAX];
121 if (!data_ready(cb_data->read_fd)) {
122 _I("Report is not ready after %d seconds.", TIMEOUT_LIVEDUMP_SEC);
123 g_dbus_method_invocation_return_error(cb_data->invocation,
126 "Report is not ready");
127 kill(cb_data->child_pid, SIGKILL);
130 if (read(cb_data->read_fd, report_path, sizeof(report_path)) == -1) {
131 _E("Read from child error: %m");
132 g_dbus_method_invocation_return_error(cb_data->invocation,
135 "Error while obtaining report path");
140 g_dbus_method_invocation_return_value(cb_data->invocation,
141 g_variant_new("(s)", report_path));
143 close(cb_data->read_fd);
145 return G_SOURCE_REMOVE;
148 static bool livedump_run(int write_fd, pid_t pid, const gchar *dump_reason)
150 char report_path[PATH_MAX];
152 if (!crash_manager_livedump_pid(pid, dump_reason, report_path, sizeof(report_path))) {
153 _E("crash_manager_livedump_pid error");
157 if (write(write_fd, report_path, strlen(report_path) + 1) == -1) {
158 _E("Write report_path error: %m");
167 static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, gchar *dump_reason)
170 * We want to run the livedump in different process so as not to
171 * block the main loop, to be able to handle many method calls
172 * in the same time, so we need a communication cannel to send
173 * back a result report path.
176 if (pipe(fds) == -1) {
177 _E("Pipe error: %m");
178 g_dbus_method_invocation_return_error(invocation,
185 struct livedump_cb_data *cb_data = (struct livedump_cb_data*)malloc(sizeof(struct livedump_cb_data));
186 if (cb_data == NULL) {
187 _E("cb_data malloc() error: %m");
190 cb_data->read_fd = fds[0];
191 cb_data->invocation = invocation;
193 GSource *source = g_timeout_source_new_seconds(TIMEOUT_LIVEDUMP_SEC);
194 cb_data->source = source;
195 g_source_add_unix_fd(source, fds[0], G_IO_IN);
196 g_source_set_callback(source, read_result_cb, cb_data, NULL);
197 g_source_attach(source, NULL);
199 pid_t child_pid = fork();
200 if (child_pid == 0) {
202 if (livedump_run(fds[1], pid, dump_reason))
206 } else if (child_pid > 0) {
207 cb_data->child_pid = child_pid;
210 _E("fork() error: %m");
211 g_dbus_method_invocation_return_error(invocation,
220 static void method_call_handler(GDBusConnection *conn,
222 const gchar *object_path,
223 const gchar *iface_name,
224 const gchar *method_name,
225 GVariant *parameters,
226 GDBusMethodInvocation *invocation,
231 if (g_strcmp0(method_name, "livedump_pid") == 0) {
235 if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(is)"))) {
236 g_variant_get(parameters, "(is)", &pid, &dump_reason);
237 livedump_pid_handler(invocation, pid, dump_reason);
239 _E("Parameters are not of the correct type (is)");
240 g_dbus_method_invocation_return_error(invocation,
243 "Parameters are not of the correct type (is)");
250 static const GDBusInterfaceVTable interface_vtable = {
256 static void on_bus_acquired(GDBusConnection *conn,
260 guint registration_id;
262 GError *error = NULL;
263 registration_id = g_dbus_connection_register_object(conn,
265 introspection_data->interfaces[0],
266 &interface_vtable, NULL, NULL, &error);
267 if (registration_id == 0 || error) {
268 _E("Failed to g_dbus_connection_register_object: %s", error ? error->message : "");
274 static void on_name_acquired(GDBusConnection *conn,
278 _D("Acquired the name %s on the system bus", name);
281 static void on_name_lost(GDBusConnection *conn,
285 _D("Lost the name %s on the system bus", name);
288 static bool dbus_init(void)
290 GError *error = NULL;
293 g_dbus_node_info_new_for_xml(introspection_xml, NULL);
294 if (introspection_data == NULL) {
295 _E("Failed to init g_dbus_info_new_for_xml");
299 GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
300 if (!conn || error) {
301 _E("Failed to get dbus: %s", error ? error->message : "");
307 g_bus_own_name(G_BUS_TYPE_SYSTEM, CRASH_BUS_NAME,
308 G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
309 on_name_acquired, on_name_lost, NULL, NULL);
316 signal(SIGCHLD, child_exit);
317 loop = g_main_loop_new(NULL, false);
320 g_main_loop_unref(loop);
324 g_mutex_init(&timeout_mutex);
327 g_main_loop_run(loop);
329 if (introspection_data)
330 g_dbus_node_info_unref(introspection_data);
331 g_mutex_clear(&timeout_mutex);
332 g_main_loop_unref(loop);