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