/*
* 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 .
*
* Authors:
* Ted Gould
*/
#include "recoverable-problem.h"
#include
#include
#include
/* 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 (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, 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;
}