Blame SOURCES/0245-daemon-trigger-dump-location-cleanup-after-detection.patch

06486d
From b963494f41fe75463a14c127e9ded5760cb09cec Mon Sep 17 00:00:00 2001
06486d
From: Jakub Filak <jfilak@redhat.com>
06486d
Date: Tue, 19 Jul 2016 20:34:02 +0200
06486d
Subject: [PATCH] daemon: trigger dump location cleanup after detection
06486d
06486d
This commit restores the old behaviour where the cleanup algorithm was
06486d
started right after new dump directory is created. This prevents piling
06486d
up of new dump directories which could lead to consumption of too much
06486d
disk space. The piling up of dump directories is currently prevented by
06486d
the plugins removing old dump directories on their own, which is in fact
06486d
problematic because the plugins don't know about each other and that causes
06486d
race conditions.
06486d
06486d
The post-create EVENT execution was moved from abrtd to abrt-server in
06486d
commit b6640620e27a029b3f1f8dcec22fb4c95e48db2a in order to replace the
06486d
inotify watch in abrtd with the /creation_notification method of
06486d
abrt-server.
06486d
06486d
What are the cases we must deal with
06486d
-----------------------------------
06486d
06486d
1) an old directory is to be removed
06486d
2) one of the queued directory is to be removed
06486d
3) currently processing directory is to be removed
06486d
06486d
The case 1) is not problematic at all (except removing directories that
06486d
are currently being handled by users).
06486d
06486d
The case 2) would cause an error message produced by abrt-handle-event
06486d
waked up from waiting for post-create.lock - the error message could be
06486d
avoided by ignoring the error in case of running post-create EVENT.
06486d
06486d
The case 3) is extremely problematic and must be avoid in all situation.
06486d
There is no other way how to avoid this case without a central
06486d
synchronization algorithm. One could claim that we should lock the
06486d
currently processed dump directory and don't removed the locked ones but
06486d
libreport's locking algorithm doesn't support recursive locking between
06486d
processes - however, the recursive inter process locking would get rid
06486d
of the case 1). Or abrt-handle-event could write the handled directory
06486d
name to a new file but it is not clear where the file would be consumed
06486d
as there is no authority doing the cleanup. And, what is the worst,
06486d
communication trough files will lead to another type race conditions.
06486d
06486d
What this patch introduces
06486d
--------------------------
06486d
06486d
This patch adds communication between abrtd and its child processes
06486d
abrt-server. When abrt-server is asked to run post-create EVENT, it
06486d
sends the "NEW_PROBLEM_DETECTED: $DUMP_DIR" message to abrtd over
06486d
STDERR. STDERR is used because STDOUT is occupied by the socket (we
06486d
might want to make it less obfuscated in future and use a FIFO
06486d
or something else, but now I am happy with using STDERR). abrtd
06486d
then pushes the abrt-server process to a queue used to track abrt-server
06486d
processes wanting to run post-create EVENT. When a process from the
06486d
queue is to be executed abrtd sends it SIGUSR1 signal. If a dump
06486d
directory of any of queued process was removed, abrtd sends the relevant
06486d
abrt-server process SIGINT signal.
06486d
06486d
Resolves #1132459
06486d
06486d
Signed-off-by: Jakub Filak <jfilak@redhat.com>
06486d
06486d
Conflicts:
06486d
	src/daemon/abrt-server.c
06486d
	src/daemon/abrtd.c
06486d
---
06486d
 src/daemon/abrt-server.c | 129 ++++++++++++++++++++
06486d
 src/daemon/abrtd.c       | 303 +++++++++++++++++++++++++++++++++++++++++++++--
06486d
 2 files changed, 420 insertions(+), 12 deletions(-)
06486d
06486d
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
06486d
index afd9fd3..a0faef6 100644
06486d
--- a/src/daemon/abrt-server.c
06486d
+++ b/src/daemon/abrt-server.c
06486d
@@ -16,6 +16,7 @@
06486d
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
06486d
 */
06486d
 #include "problem_api.h"
06486d
+#include "abrt_glib.h"
06486d
 #include "libabrt.h"
06486d
 
06486d
 /* Maximal length of backtrace. */
06486d
@@ -71,10 +72,75 @@ MANDATORY ITEMS:
06486d
 You can send more messages using the same KEY=value format.
06486d
 */
06486d
 
06486d
+static int g_signal_pipe[2];
06486d
+
06486d
+struct waiting_context
06486d
+{
06486d
+    GMainLoop *main_loop;
06486d
+    const char *dirname;
06486d
+    int retcode;
06486d
+    enum abrt_daemon_reply
06486d
+    {
06486d
+        ABRT_CONTINUE,
06486d
+        ABRT_INTERRUPT,
06486d
+    } reply;
06486d
+};
06486d
+
06486d
 static unsigned total_bytes_read = 0;
06486d
 
06486d
 static uid_t client_uid = (uid_t)-1L;
06486d
 
06486d
+static void
06486d
+handle_signal(int signo)
06486d
+{
06486d
+    int save_errno = errno;
06486d
+    uint8_t sig_caught = signo;
06486d
+    if (write(g_signal_pipe[1], &sig_caught, 1))
06486d
+        /* we ignore result, if () shuts up stupid compiler */;
06486d
+    errno = save_errno;
06486d
+}
06486d
+
06486d
+static gboolean
06486d
+handle_signal_pipe_cb(GIOChannel *gio, GIOCondition condition, gpointer user_data)
06486d
+{
06486d
+    gsize len = 0;
06486d
+    uint8_t signals[2];
06486d
+
06486d
+    for (;;)
06486d
+    {
06486d
+        GError *error = NULL;
06486d
+        GIOStatus stat = g_io_channel_read_chars(gio, (void *)signals, sizeof(signals), &len, NULL);
06486d
+        if (stat == G_IO_STATUS_ERROR)
06486d
+            error_msg_and_die(_("Can't read from gio channel: '%s'"), error ? error->message : "");
06486d
+        if (stat == G_IO_STATUS_EOF)
06486d
+            return FALSE; /* Remove this GLib source */
06486d
+        if (stat == G_IO_STATUS_AGAIN)
06486d
+            break;
06486d
+
06486d
+        /* G_IO_STATUS_NORMAL */
06486d
+        for (unsigned signo = 0; signo < len; ++signo)
06486d
+        {
06486d
+            /* we did receive a signal */
06486d
+            struct waiting_context *context = (struct waiting_context *)user_data;
06486d
+            log_debug("Got signal %d through signal pipe", signals[signo]);
06486d
+            switch (signals[signo])
06486d
+            {
06486d
+                case SIGUSR1: context->reply = ABRT_CONTINUE; break;
06486d
+                case SIGINT:  context->reply = ABRT_INTERRUPT; break;
06486d
+                default:
06486d
+                {
06486d
+                    error_msg("Bug - aborting - unsupported signal: %d", signals[signo]);
06486d
+                    abort();
06486d
+                }
06486d
+            }
06486d
+
06486d
+            g_main_loop_quit(context->main_loop);
06486d
+            return FALSE; /* remove this event */
06486d
+        }
06486d
+    }
06486d
+
06486d
+    return TRUE; /* "please don't remove this event" */
06486d
+}
06486d
 
06486d
 /* Remove dump dir */
06486d
 static int delete_path(const char *dump_dir_name)
06486d
@@ -153,6 +219,24 @@ static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *ev
06486d
     return child;
06486d
 }
06486d
 
06486d
+static gboolean emit_new_problem_signal(gpointer data)
06486d
+{
06486d
+    struct waiting_context *context = (struct waiting_context *)data;
06486d
+
06486d
+    const size_t wrote = fprintf(stderr, "NEW_PROBLEM_DETECTED: %s\n", context->dirname);
06486d
+    fflush(stderr);
06486d
+
06486d
+    if (wrote <= 0)
06486d
+    {
06486d
+        error_msg("Failed to communicate with the daemon");
06486d
+        context->retcode = 503;
06486d
+        g_main_loop_quit(context->main_loop);
06486d
+    }
06486d
+
06486d
+    log_notice("Emitted new problem signal, waiting for SIGUSR1|SIGINT");
06486d
+    return FALSE;
06486d
+}
06486d
+
06486d
 static int run_post_create(const char *dirname)
06486d
 {
06486d
     /* If doesn't start with "g_settings_dump_location/"... */
06486d
@@ -179,6 +263,51 @@ static int run_post_create(const char *dirname)
06486d
         }
06486d
     }
06486d
 
06486d
+    /*
06486d
+     * The post-create event cannot be run concurrently for more problem
06486d
+     * directories. The problem is in searching for duplicates process
06486d
+     * in case when two concurrently processed directories are duplicates
06486d
+     * of each other. Both of the directories are marked as duplicates
06486d
+     * of each other and are deleted.
06486d
+     */
06486d
+    log_debug("Creating glib main loop");
06486d
+    struct waiting_context context = {0};
06486d
+    context.main_loop = g_main_loop_new(NULL, FALSE);
06486d
+    context.dirname = dirname;
06486d
+
06486d
+    log_debug("Setting up a signal handler");
06486d
+    /* Set up signal pipe */
06486d
+    xpipe(g_signal_pipe);
06486d
+    close_on_exec_on(g_signal_pipe[0]);
06486d
+    close_on_exec_on(g_signal_pipe[1]);
06486d
+    ndelay_on(g_signal_pipe[0]);
06486d
+    ndelay_on(g_signal_pipe[1]);
06486d
+    signal(SIGUSR1, handle_signal);
06486d
+    signal(SIGINT, handle_signal);
06486d
+    GIOChannel *channel_signal = abrt_gio_channel_unix_new(g_signal_pipe[0]);
06486d
+    g_io_add_watch(channel_signal, G_IO_IN | G_IO_PRI, handle_signal_pipe_cb, &context);
06486d
+
06486d
+    g_idle_add(emit_new_problem_signal, &context);
06486d
+
06486d
+    g_main_loop_run(context.main_loop);
06486d
+
06486d
+    g_main_loop_unref(context.main_loop);
06486d
+    g_io_channel_unref(channel_signal);
06486d
+    close(g_signal_pipe[1]);
06486d
+    close(g_signal_pipe[0]);
06486d
+
06486d
+    log_notice("Waiting finished");
06486d
+
06486d
+    if (context.retcode != 0)
06486d
+        return context.retcode;
06486d
+
06486d
+    if (context.reply != ABRT_CONTINUE)
06486d
+        /* The only reason for the interruption is removed problem directory */
06486d
+        return 413;
06486d
+    /*
06486d
+     * The post-create event synchronization done.
06486d
+     */
06486d
+
06486d
     int child_stdout_fd;
06486d
     int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd);
06486d
 
06486d
diff --git a/src/daemon/abrtd.c b/src/daemon/abrtd.c
06486d
index b79e940..ff0565c 100644
06486d
--- a/src/daemon/abrtd.c
06486d
+++ b/src/daemon/abrtd.c
06486d
@@ -55,9 +55,42 @@ static int s_signal_pipe_write = -1;
06486d
 static unsigned s_timeout;
06486d
 static bool s_exiting;
06486d
 
06486d
+GList *s_processes;
06486d
+GList *s_dir_queue;
06486d
+
06486d
 static GIOChannel *channel_socket = NULL;
06486d
 static guint channel_id_socket = 0;
06486d
-static int child_count = 0;
06486d
+
06486d
+struct abrt_server_proc
06486d
+{
06486d
+    pid_t pid;
06486d
+    int fdout;
06486d
+    char *dirname;
06486d
+    GIOChannel *channel;
06486d
+    guint watch_id;
06486d
+    enum {
06486d
+        AS_UKNOWN,
06486d
+        AS_POST_CREATE,
06486d
+    } type;
06486d
+};
06486d
+
06486d
+/* Returns 0 if proc's pid equals the the given pid */
06486d
+static gint abrt_server_compare_pid(struct abrt_server_proc *proc, pid_t *pid)
06486d
+{
06486d
+    return proc->pid != *pid;
06486d
+}
06486d
+
06486d
+/* Returns 0 if proc's fdout equals the the given fdout */
06486d
+static gint abrt_server_compare_fdout(struct abrt_server_proc *proc, int *fdout)
06486d
+{
06486d
+    return proc->fdout != *fdout;
06486d
+}
06486d
+
06486d
+/* Returns 0 if proc's dirname equals the the given dirname */
06486d
+static gint abrt_server_compare_dirname(struct abrt_server_proc *proc, const char *dirname)
06486d
+{
06486d
+    return g_strcmp0(proc->dirname, dirname);
06486d
+}
06486d
 
06486d
 /* Helpers */
06486d
 static guint add_watch_or_die(GIOChannel *channel, unsigned condition, GIOFunc func)
06486d
@@ -69,9 +102,212 @@ static guint add_watch_or_die(GIOChannel *channel, unsigned condition, GIOFunc f
06486d
     return r;
06486d
 }
06486d
 
06486d
-static void increment_child_count(void)
06486d
+static void stop_abrt_server(struct abrt_server_proc *proc)
06486d
+{
06486d
+    kill(proc->pid, SIGINT);
06486d
+}
06486d
+
06486d
+static void dispose_abrt_server(struct abrt_server_proc *proc)
06486d
+{
06486d
+    close(proc->fdout);
06486d
+    free(proc->dirname);
06486d
+
06486d
+    if (proc->watch_id > 0)
06486d
+        g_source_remove(proc->watch_id);
06486d
+
06486d
+    if (proc->channel != NULL)
06486d
+        g_io_channel_unref(proc->channel);
06486d
+}
06486d
+
06486d
+static void notify_next_post_create_process(struct abrt_server_proc *finished)
06486d
+{
06486d
+    if (finished != NULL)
06486d
+        s_dir_queue = g_list_remove(s_dir_queue, finished);
06486d
+
06486d
+    while (s_dir_queue != NULL)
06486d
+    {
06486d
+        struct abrt_server_proc *n = (struct abrt_server_proc *)s_dir_queue->data;
06486d
+        if (n->type == AS_POST_CREATE)
06486d
+            break;
06486d
+
06486d
+        if (kill(n->pid, SIGUSR1) >= 0)
06486d
+        {
06486d
+            n->type = AS_POST_CREATE;
06486d
+            break;
06486d
+        }
06486d
+
06486d
+        /* This could happen only if the notified process disappeared - crashed?
06486d
+         */
06486d
+        perror_msg("Failed to send SIGUSR1 to %d", n->pid);
06486d
+        log_warning("Directory '%s' will not be processed", n->dirname);
06486d
+
06486d
+        /* Remove the problematic process from the post-crate directory queue
06486d
+         * and go to try to notify another process.
06486d
+         */
06486d
+        s_dir_queue = g_list_delete_link(s_dir_queue, s_dir_queue);
06486d
+    }
06486d
+}
06486d
+
06486d
+/* Queueing the process will also lead to cleaning up the dump location.
06486d
+ */
06486d
+static void queue_post_craete_process(struct abrt_server_proc *proc)
06486d
+{
06486d
+    load_abrt_conf();
06486d
+    struct abrt_server_proc *running = s_dir_queue == NULL ? NULL
06486d
+                                                           : (struct abrt_server_proc *)s_dir_queue->data;
06486d
+    if (g_settings_nMaxCrashReportsSize == 0)
06486d
+        goto consider_processing;
06486d
+
06486d
+    const char *full_path_ignored = running != NULL ? running->dirname
06486d
+                                                    : proc->dirname;
06486d
+    const char *ignored = strrchr(full_path_ignored, '/');
06486d
+    if (NULL == ignored)
06486d
+        /* Paranoia, this should not happen. */
06486d
+        ignored = full_path_ignored;
06486d
+    else
06486d
+        /* Move behind '/' */
06486d
+        ++ignored;
06486d
+
06486d
+    char *worst_dir = NULL;
06486d
+    const double max_size = 1024 * 1024 * g_settings_nMaxCrashReportsSize;
06486d
+    while (get_dirsize_find_largest_dir(g_settings_dump_location, &worst_dir, ignored) >= max_size
06486d
+           && worst_dir)
06486d
+    {
06486d
+        const char *kind = "old";
06486d
+        char *deleted = concat_path_file(g_settings_dump_location, worst_dir);
06486d
+
06486d
+        GList *proc_of_deleted_item = NULL;
06486d
+        if (proc != NULL && strcmp(deleted, proc->dirname) == 0)
06486d
+        {
06486d
+            kind = "new";
06486d
+            stop_abrt_server(proc);
06486d
+            proc = NULL;
06486d
+        }
06486d
+        else if ((proc_of_deleted_item = g_list_find_custom(s_dir_queue, deleted, (GCompareFunc)abrt_server_compare_dirname)))
06486d
+        {
06486d
+            kind = "unprocessed";
06486d
+            struct abrt_server_proc *removed_proc = (struct abrt_server_proc *)proc_of_deleted_item->data;
06486d
+            s_dir_queue = g_list_delete_link(s_dir_queue, proc_of_deleted_item);
06486d
+            stop_abrt_server(removed_proc);
06486d
+        }
06486d
+
06486d
+        log("Size of '%s' >= %u MB (MaxCrashReportsSize), deleting %s directory '%s'",
06486d
+                g_settings_dump_location, g_settings_nMaxCrashReportsSize,
06486d
+                kind, worst_dir);
06486d
+
06486d
+        free(worst_dir);
06486d
+        worst_dir = NULL;
06486d
+
06486d
+        struct dump_dir *dd = dd_opendir(deleted, DD_FAIL_QUIETLY_ENOENT);
06486d
+        if (dd != NULL)
06486d
+            dd_delete(dd);
06486d
+
06486d
+        free(deleted);
06486d
+    }
06486d
+
06486d
+consider_processing:
06486d
+    /* If the process survived cleaning up the dump location, append it to the
06486d
+     * post-create queue.
06486d
+     */
06486d
+    if (proc != NULL)
06486d
+        s_dir_queue = g_list_append(s_dir_queue, proc);
06486d
+
06486d
+    /* If there were no running post-crate process before we added the
06486d
+     * currently handled process to the post-create queue, start processing of
06486d
+     * the currently handled process.
06486d
+     */
06486d
+    if (running == NULL)
06486d
+        notify_next_post_create_process(NULL/*finished*/);
06486d
+}
06486d
+
06486d
+static gboolean abrt_server_output_cb(GIOChannel *channel, GIOCondition condition, gpointer user_data)
06486d
+{
06486d
+    int fdout = g_io_channel_unix_get_fd(channel);
06486d
+    GList *item = g_list_find_custom(s_processes, &fdout, (GCompareFunc)abrt_server_compare_fdout);
06486d
+    if (item == NULL)
06486d
+    {
06486d
+        log_warning("Closing a pipe fd (%d) without a process assigned", fdout);
06486d
+        close(fdout);
06486d
+        return FALSE;
06486d
+    }
06486d
+
06486d
+    struct abrt_server_proc *proc = (struct abrt_server_proc *)item->data;
06486d
+
06486d
+    if (condition & G_IO_HUP)
06486d
+    {
06486d
+        log_debug("abrt-server(%d) closed its pipe", proc->pid);
06486d
+        proc->watch_id = 0;
06486d
+        return FALSE;
06486d
+    }
06486d
+
06486d
+    for (;;)
06486d
+    {
06486d
+        gchar *line;
06486d
+        gsize len = 0;
06486d
+        gsize pos = 0;
06486d
+        GError *error = NULL;
06486d
+
06486d
+        /* We use buffered channel so we do not need to read from the channel in a
06486d
+         * loop */
06486d
+        GIOStatus stat = g_io_channel_read_line(channel, &line, &len, &pos, &error);
06486d
+        if (stat == G_IO_STATUS_ERROR)
06486d
+            error_msg_and_die("Can't read from pipe of abrt-server(%d): '%s'", proc->pid, error ? error->message : "");
06486d
+        if (stat == G_IO_STATUS_EOF)
06486d
+        {
06486d
+            log_debug("abrt-server(%d)'s output read till end", proc->pid);
06486d
+            proc->watch_id = 0;
06486d
+            return FALSE; /* Remove this event */
06486d
+        }
06486d
+        if (stat == G_IO_STATUS_AGAIN)
06486d
+            break;
06486d
+
06486d
+        /* G_IO_STATUS_NORMAL) */
06486d
+        line[pos] = '\0';
06486d
+        if (g_str_has_prefix(line, "NEW_PROBLEM_DETECTED: "))
06486d
+        {
06486d
+            if (proc->dirname != NULL)
06486d
+            {
06486d
+                log_warning("abrt-server(%d): already handling: %s", proc->pid, proc->dirname);
06486d
+                free(proc->dirname);
06486d
+                /* Because process can be only once in the dir queue */
06486d
+                s_dir_queue = g_list_remove(s_dir_queue, proc);
06486d
+            }
06486d
+
06486d
+            proc->dirname = xstrdup(line + strlen("NEW_PROBLEM_DETECTED: "));
06486d
+            log_notice("abrt-server(%d): handling new problem: %s", proc->pid, proc->dirname);
06486d
+            queue_post_craete_process(proc);
06486d
+        }
06486d
+        else
06486d
+            log("abrt-server(%d): not recognized message: '%s'", proc->pid, line);
06486d
+
06486d
+        g_free(line);
06486d
+    }
06486d
+
06486d
+    return TRUE; /* Keep this event */
06486d
+}
06486d
+
06486d
+static void add_abrt_server_proc(const pid_t pid, int fdout)
06486d
 {
06486d
-    if (++child_count >= MAX_CLIENT_COUNT)
06486d
+    struct abrt_server_proc *proc = xmalloc(sizeof(*proc));
06486d
+    proc->pid = pid;
06486d
+    proc->fdout = fdout;
06486d
+    proc->dirname = NULL;
06486d
+    proc->type = AS_UKNOWN;
06486d
+    proc->channel = abrt_gio_channel_unix_new(proc->fdout);
06486d
+    proc->watch_id = g_io_add_watch(proc->channel,
06486d
+                                    G_IO_IN | G_IO_HUP,
06486d
+                                    abrt_server_output_cb,
06486d
+                                    proc);
06486d
+
06486d
+    GError *error = NULL;
06486d
+    g_io_channel_set_flags(proc->channel, G_IO_FLAG_NONBLOCK, &error);
06486d
+    if (error != NULL)
06486d
+        error_msg_and_die("g_io_channel_set_flags failed: '%s'", error->message);
06486d
+
06486d
+    g_io_channel_set_buffered(proc->channel, TRUE);
06486d
+
06486d
+    s_processes = g_list_append(s_processes, proc);
06486d
+    if (g_list_length(s_processes) >= MAX_CLIENT_COUNT)
06486d
     {
06486d
         error_msg("Too many clients, refusing connections to '%s'", SOCKET_FILE);
06486d
         /* To avoid infinite loop caused by the descriptor in "ready" state,
06486d
@@ -84,11 +320,29 @@ static void increment_child_count(void)
06486d
 
06486d
 static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpointer ptr_unused);
06486d
 
06486d
-static void decrement_child_count(void)
06486d
+static void remove_abrt_server_proc(pid_t pid, int status)
06486d
 {
06486d
-    if (child_count)
06486d
-        child_count--;
06486d
-    if (child_count < MAX_CLIENT_COUNT && !channel_id_socket)
06486d
+    GList *item = g_list_find_custom(s_processes, &pid, (GCompareFunc)abrt_server_compare_pid);
06486d
+    if (item == NULL)
06486d
+        return;
06486d
+
06486d
+    struct abrt_server_proc *proc = (struct abrt_server_proc *)item->data;
06486d
+    item->data = NULL;
06486d
+    s_processes = g_list_delete_link(s_processes, item);
06486d
+
06486d
+    if (proc->type == AS_POST_CREATE)
06486d
+        notify_next_post_create_process(proc);
06486d
+    else
06486d
+    {   /* Make sure out-of-order exited abrt-server post-create processes do
06486d
+         * not stay in the post-create queue.
06486d
+         */
06486d
+        s_dir_queue = g_list_remove(s_dir_queue, proc);
06486d
+    }
06486d
+
06486d
+    dispose_abrt_server(proc);
06486d
+    free(proc);
06486d
+
06486d
+    if (g_list_length(s_processes) < MAX_CLIENT_COUNT && !channel_id_socket)
06486d
     {
06486d
         log_info("Accepting connections on '%s'", SOCKET_FILE);
06486d
         channel_id_socket = add_watch_or_die(channel_socket, G_IO_IN | G_IO_PRI | G_IO_HUP, server_socket_cb);
06486d
@@ -107,17 +361,27 @@ static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpo
06486d
 
06486d
     log_notice("New client connected");
06486d
     fflush(NULL); /* paranoia */
06486d
+
06486d
+    int pipefd[2];
06486d
+    xpipe(pipefd);
06486d
+
06486d
     pid_t pid = fork();
06486d
     if (pid < 0)
06486d
     {
06486d
         perror_msg("fork");
06486d
+        close(pipefd[0]);
06486d
+        close(pipefd[1]);
06486d
         close(socket);
06486d
         return TRUE;
06486d
     }
06486d
     if (pid == 0) /* child */
06486d
     {
06486d
-        xmove_fd(socket, 0);
06486d
-        xdup2(0, 1);
06486d
+        xdup2(socket, STDIN_FILENO);
06486d
+        xdup2(socket, STDOUT_FILENO);
06486d
+        close(socket);
06486d
+
06486d
+        close(pipefd[0]);
06486d
+        xmove_fd(pipefd[1], STDERR_FILENO);
06486d
 
06486d
         char *argv[3];  /* abrt-server [-s] NULL */
06486d
         char **pp = argv;
06486d
@@ -129,9 +393,12 @@ static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpo
06486d
         execvp(argv[0], argv);
06486d
         perror_msg_and_die("Can't execute '%s'", argv[0]);
06486d
     }
06486d
+
06486d
     /* parent */
06486d
-    increment_child_count();
06486d
     close(socket);
06486d
+    close(pipefd[1]);
06486d
+    add_abrt_server_proc(pid, pipefd[0]);
06486d
+
06486d
     return TRUE;
06486d
 }
06486d
 
06486d
@@ -149,9 +416,21 @@ static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpoint
06486d
             s_exiting = 1;
06486d
         else
06486d
         {
06486d
-            while (safe_waitpid(-1, NULL, WNOHANG) > 0)
06486d
+            pid_t cpid;
06486d
+            int status;
06486d
+            while ((cpid = safe_waitpid(-1, &status, WNOHANG)) > 0)
06486d
             {
06486d
-                decrement_child_count();
06486d
+                if (WIFSIGNALED(status))
06486d
+                    log_debug("abrt-server(%d) signaled with %d", cpid, WTERMSIG(status));
06486d
+                else if (WIFEXITED(status))
06486d
+                    log_debug("abrt-server(%d) exited with %d", cpid, WEXITSTATUS(status));
06486d
+                else
06486d
+                {
06486d
+                    log_debug("abrt-server(%d) is being debugged", cpid);
06486d
+                    continue;
06486d
+                }
06486d
+
06486d
+                remove_abrt_server_proc(cpid, status);
06486d
             }
06486d
         }
06486d
     }
06486d
-- 
06486d
1.8.3.1
06486d