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

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