diff options
Diffstat (limited to 'src/recoverable-problem.c')
-rw-r--r-- | src/recoverable-problem.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/src/recoverable-problem.c b/src/recoverable-problem.c new file mode 100644 index 0000000..7aff163 --- /dev/null +++ b/src/recoverable-problem.c @@ -0,0 +1,167 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Ted Gould <ted.gould@canonical.com> + */ + +#include "recoverable-problem.h" +#include <glib/gstdio.h> +#include <string.h> +#include <errno.h> + +/* Helpers to ensure we write nicely */ +static void +write_string (int fd, + const gchar *string) +{ + int res; + do + res = write (fd, string, strlen (string)); + while (G_UNLIKELY (res == -1 && errno == EINTR)); +} + +/* Make NULLs fast and fun! */ +static void +write_null (int fd) +{ + int res; + do + res = write (fd, "", 1); + while (G_UNLIKELY (res == -1 && errno == EINTR)); +} + +/* Child watcher */ +static gboolean +apport_child_watch (GPid pid G_GNUC_UNUSED, gint status G_GNUC_UNUSED, gpointer user_data) +{ + g_main_loop_quit((GMainLoop *)user_data); + return FALSE; +} + +static gboolean +apport_child_timeout (gpointer user_data) +{ + g_warning("Recoverable Error Reporter Timeout"); + g_main_loop_quit((GMainLoop *)user_data); + return FALSE; +} + +/* Code to report an error */ +void +report_recoverable_problem (const gchar * signature, GPid report_pid, gboolean wait, gchar * additional_properties[]) +{ + GSpawnFlags flags; + gboolean first; + GError * error = NULL; + gint error_stdin = 0; + GPid pid = 0; + gchar * pid_str = NULL; + gchar ** argv = NULL; + gchar * argv_nopid[2] = { + "/usr/share/apport/recoverable_problem", + NULL + }; + gchar * argv_pid[4] = { + "/usr/share/apport/recoverable_problem", + "-p", + NULL, /* put pid_str when allocated here */ + NULL + }; + + + argv = (gchar **)argv_nopid; + + if (report_pid != 0) { + pid_str = g_strdup_printf("%d", report_pid); + argv_pid[2] = pid_str; + argv = (gchar**)argv_pid; + } + + flags = G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL; + if (wait) { + flags |= G_SPAWN_DO_NOT_REAP_CHILD; + } + + g_spawn_async_with_pipes(NULL, /* cwd */ + argv, + NULL, /* envp */ + flags, + NULL, NULL, /* child setup func */ + &pid, + &error_stdin, + NULL, /* stdout */ + NULL, /* stderr */ + &error); + + if (error != NULL) { + g_warning("Unable to report a recoverable error: %s", error->message); + g_error_free(error); + } + + first = TRUE; + + if (error_stdin != 0 && signature != NULL) { + write_string(error_stdin, "DuplicateSignature"); + write_null(error_stdin); + write_string(error_stdin, signature); + + first = FALSE; + } + + if (error_stdin != 0 && additional_properties != NULL) { + gint i; + for (i = 0; additional_properties[i] != NULL; i++) { + if (!first) { + write_null(error_stdin); + } else { + first = FALSE; + } + + write_string(error_stdin, additional_properties[i]); + } + } + + if (error_stdin != 0) { + close(error_stdin); + } + + if (wait && pid != 0) { + GSource * child_source, * timeout_source; + GMainContext * context = g_main_context_new(); + GMainLoop * loop = g_main_loop_new(context, FALSE); + + child_source = g_child_watch_source_new(pid); + g_source_attach(child_source, context); + g_source_set_callback(child_source, (GSourceFunc)apport_child_watch, loop, NULL); + + timeout_source = g_timeout_source_new_seconds(5); + g_source_attach(timeout_source, context); + g_source_set_callback(timeout_source, apport_child_timeout, loop, NULL); + + g_main_loop_run(loop); + + g_source_destroy(timeout_source); + g_source_destroy(child_source); + g_main_loop_unref(loop); + g_main_context_unref(context); + + g_spawn_close_pid(pid); + } + + g_free(pid_str); + + return; +} |