>From 3541ca96abd7528f73dc676e897a212152124811 Mon Sep 17 00:00:00 2001
From: karel <karel@localhost.localdomain>
Date: Fri, 14 May 2010 17:13:53 +0200
Subject: [PATCH 2/5] Daemon socket for reporting crashes

---
 src/Daemon/Daemon.cpp                  |    5 +
 src/Daemon/Makefile.am                 |    3 +-
 src/Daemon/dumpsocket.cpp              |  573 ++++++++++++++++++++++++++++++++
 src/Daemon/dumpsocket.h                |   81 +++++
 src/Hooks/Makefile.am                  |    5 +-
 src/Hooks/abrt_exception_handler.py.in |   25 +-
 6 files changed, 680 insertions(+), 12 deletions(-)
 create mode 100644 src/Daemon/dumpsocket.cpp
 create mode 100644 src/Daemon/dumpsocket.h

diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp
index 3d8df57..d79d90a 100644
--- a/src/Daemon/Daemon.cpp
+++ b/src/Daemon/Daemon.cpp
@@ -41,6 +41,7 @@
 #include "CrashWatcher.h"
 #include "DebugDump.h"
 #include "Daemon.h"
+#include "dumpsocket.h"
 
 using namespace std;
 
@@ -848,6 +849,9 @@ int main(int argc, char** argv)
             throw 1;
         pidfile_created = true;
 
+        /* Open socket to receive new crashes. */
+        dumpsocket_init();
+
         /* Note: this already may process a few dbus messages,
          * therefore it should be the last thing to initialize.
          */
@@ -899,6 +903,7 @@ int main(int argc, char** argv)
     /* Error or INT/TERM. Clean up, in reverse order.
      * Take care to not undo things we did not do.
      */
+    dumpsocket_shutdown();
     if (pidfile_created)
         unlink(VAR_RUN_PIDFILE);
     if (lockfile_created)
diff --git a/src/Daemon/Makefile.am b/src/Daemon/Makefile.am
index 382f104..5629215 100644
--- a/src/Daemon/Makefile.am
+++ b/src/Daemon/Makefile.am
@@ -10,7 +10,8 @@ abrtd_SOURCES = \
     CommLayerServer.h CommLayerServer.cpp \
     CommLayerServerDBus.h CommLayerServerDBus.cpp \
     Daemon.h Daemon.cpp \
-    Settings.h Settings.cpp
+    Settings.h Settings.cpp \
+    dumpsocket.h dumpsocket.cpp
 abrtd_CPPFLAGS = \
     -I$(srcdir)/../../inc \
     -I$(srcdir)/../../lib/Utils \
diff --git a/src/Daemon/dumpsocket.cpp b/src/Daemon/dumpsocket.cpp
new file mode 100644
index 0000000..f1171ea
--- /dev/null
+++ b/src/Daemon/dumpsocket.cpp
@@ -0,0 +1,573 @@
+/*
+  Copyright (C) 2010  ABRT team
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY 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, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "dumpsocket.h"
+#include "abrtlib.h"
+#include <glib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include "DebugDump.h"
+#include "CrashTypes.h"
+#include "ABRTException.h"
+#include "hooklib.h"
+#include "strbuf.h"
+
+#define SOCKET_FILE VAR_RUN"/abrt.socket"
+#define SOCKET_PERMISSION 0666
+
+/* Maximal length of backtrace. */
+#define MAX_BACKTRACE_SIZE (1024*1024)
+
+/* Amount of data received from one client for a message before reporting error. */
+#define MAX_MESSAGE_SIZE (4*MAX_BACKTRACE_SIZE)
+
+/* Maximum number of simultaneously opened client connections. */
+#define MAX_CLIENT_COUNT 10
+
+/* Interval between checks of client halt, in seconds. */
+#define CLIENT_CHECK_INTERVAL 10
+
+/* Interval with no data received from client, after which the client is
+   considered halted, in seconds. */
+#define CLIENT_HALT_INTERVAL 10
+
+/* Maximal number of characters read from socket at once. */
+#define INPUT_BUFFER_SIZE 1024
+
+static GIOChannel *channel = NULL;
+static guint channel_cb_id = 0;
+static int client_count = 0;
+
+/* Information about single socket session. */
+struct client
+{
+    /* Client user id */
+    uid_t uid;
+    /* Buffer for incomplete incoming messages. */
+    GByteArray *messagebuf;
+    /* Executable. */
+    char *executable;
+    /* Process ID. */
+    int pid;
+    char *backtrace;
+    /* Python, Ruby etc. */
+    char *analyzer;
+    /* Directory base name: python (or pyhook), ruby etc. */
+    char *basename;
+    /* Crash reason.
+     * Python example: "CCMainWindow.py:1:<module>:ZeroDivisionError:
+     *                 integer division or modulo by zero"
+     */
+    char *reason;
+    /* Last time some data were received over the socket
+     * from the client.
+     */
+    time_t lastwrite;
+    /* Timer checking client halt. */
+    guint timer_id;
+    /* Client socket callback id. */
+    guint socket_id;
+    /* Client socket channel */
+    GIOChannel *channel;
+};
+
+static gboolean server_socket_cb(GIOChannel *source,
+                                 GIOCondition condition,
+                                 gpointer data);
+
+/* Releases all memory that belongs to a client session. */
+static void client_free(struct client *client)
+{
+    /* Delete the uncompleted message if there is some. */
+    g_byte_array_free(client->messagebuf, TRUE);
+    free(client->executable);
+    free(client->backtrace);
+    free(client->analyzer);
+    free(client->basename);
+    free(client->reason);
+    g_source_remove(client->timer_id);
+    g_source_remove(client->socket_id);
+    g_io_channel_unref(client->channel);
+    free(client);
+    --client_count;
+    if (!channel_cb_id)
+    {
+        channel_cb_id = g_io_add_watch(channel,
+                                       (GIOCondition)(G_IO_IN | G_IO_PRI),
+                                       (GIOFunc)server_socket_cb,
+                                       NULL);
+        if (!channel_cb_id)
+            perror_msg_and_die("dumpsocket: Can't add socket watch");
+    }
+}
+
+/* Callback called by glib main loop at regular intervals when
+   some client is connected. */
+static gboolean client_check_cb(gpointer data)
+{
+    struct client *client = (struct client*)data;
+    if (time(NULL) - client->lastwrite > CLIENT_HALT_INTERVAL)
+    {
+        log("dumpsocket: client socket timeout reached, closing connection");
+        client_free(client);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/* Caller is responsible to free() the returned value. */
+static char *giocondition_to_string(GIOCondition condition)
+{
+    struct strbuf *strbuf = strbuf_new();
+    if (condition & G_IO_HUP)
+        strbuf_append_str(strbuf, "G_IO_HUP | ");
+    if (condition & G_IO_ERR)
+        strbuf_append_str(strbuf, "G_IO_ERR | ");
+    if (condition & G_IO_NVAL)
+        strbuf_append_str(strbuf, "G_IO_NVAL | ");
+    if (condition & G_IO_IN)
+        strbuf_append_str(strbuf, "G_IO_IN | ");
+    if (condition & G_IO_OUT)
+        strbuf_append_str(strbuf, "G_IO_OUT | ");
+    if (condition & G_IO_PRI)
+        strbuf_append_str(strbuf, "G_IO_PRI | ");
+    if (strbuf->len == 0)
+        strbuf_append_str(strbuf, "none");
+    else
+    {
+        /* remove the last " | " */
+        strbuf->len -= 3;
+        strbuf->buf[strbuf->len] = '\0';
+    }
+    char *result = strbuf->buf;
+    strbuf_free_nobuf(strbuf);
+    return result;
+}
+
+/* Create a new debug dump from client session.
+ * Caller must ensure that all fields in struct client
+ * are properly filled.
+ */
+static void create_debug_dump(struct client *client)
+{
+    /* Create temp directory with the debug dump.
+       This directory is renamed to final directory name after
+       all files have been stored into it.
+    */
+    char *path = xasprintf(DEBUG_DUMPS_DIR"/%s-%ld-%u.new",
+                           client->basename,
+                           (long)time(NULL),
+                           client->pid);
+    /* No need to check the path length, as all variables used are limited, and dd.Create()
+       fails if the path is too long. */
+
+    CDebugDump dd;
+    try {
+        dd.Create(path, client->uid);
+    } catch (CABRTException &e) {
+        dd.Delete();
+        dd.Close();
+        error_msg_and_die("dumpsocket: Error while creating crash dump %s: %s", path, e.what());
+    }
+
+    dd.SaveText(FILENAME_ANALYZER, client->analyzer);
+    dd.SaveText(FILENAME_EXECUTABLE, client->executable);
+    dd.SaveText(FILENAME_BACKTRACE, client->backtrace);
+    dd.SaveText(FILENAME_REASON, client->reason);
+
+    /* Obtain and save the command line. */
+    char *cmdline = get_cmdline(client->pid); // never NULL
+    dd.SaveText(FILENAME_CMDLINE, cmdline);
+    free(cmdline);
+
+    /* Store id of the user whose application crashed. */
+    char uid_str[sizeof(long) * 3 + 2];
+    sprintf(uid_str, "%lu", (long)client->uid);
+    dd.SaveText(CD_UID, uid_str);
+
+    dd.Close();
+
+    /* Move the completely created debug dump to
+       final directory. */
+    char *newpath = xstrndup(path, strlen(path) - strlen(".new"));
+    if (rename(path, newpath) == 0)
+        strcpy(path, newpath);
+    free(newpath);
+
+    log("dumpsocket: Saved %s crash dump of pid %u to %s",
+        client->analyzer, client->pid, path);
+
+    /* Handle free space checking. */
+    unsigned maxCrashReportsSize = 0;
+    parse_conf(NULL, &maxCrashReportsSize, NULL, NULL);
+    if (maxCrashReportsSize > 0)
+    {
+        check_free_space(maxCrashReportsSize);
+        trim_debug_dumps(maxCrashReportsSize, path);
+    }
+
+    free(path);
+}
+
+/* Checks if a string contains only printable characters. */
+static bool printable_str(const char *str)
+{
+    do {
+        if ((unsigned char)(*str) < ' ' || *str == 0x7f)
+            return false;
+        str++;
+    } while (*str);
+    return true;
+}
+
+/* Checks if a string has certain prefix. */
+static bool starts_with(const char *str, const char *start)
+{
+    return strncmp(str, start, strlen(start)) == 0;
+}
+
+/* @returns
+ *  Caller is responsible to call free() on the returned
+ *  pointer.
+ *  If NULL is returned, string extraction failed.
+ */
+static char *try_to_get_string(const char *message,
+                               const char *tag,
+                               size_t max_len,
+                               bool printable,
+                               bool allow_slashes)
+{
+    if (!starts_with(message, tag))
+        return NULL;
+
+    const char *contents = message + strlen(tag);
+    if ((printable && !printable_str(contents)) ||
+        (!allow_slashes && strchr(contents, '/')))
+    {
+        error_msg("dumpsocket: Received %s contains invalid characters -> skipping", tag);
+        return NULL;
+    }
+
+    if (strlen(contents) > max_len)
+    {
+        char *max_len_str = g_format_size_for_display(max_len);
+        error_msg("dumpsocket: Received %s too long -> trimming to %s", tag, max_len_str);
+        g_free(max_len_str);
+    }
+
+    return xstrndup(contents, max_len);
+}
+
+/* Handles a message received from client over socket. */
+static void process_message(struct client *client, const char *message)
+{
+/* @param tag
+ *  The message identifier. Message starting with it
+ *  is handled by this macro.
+ * @param field
+ *  Member in struct client, which should be filled by
+ *  the field contents.
+ * @param max_len
+ *  Maximum length of the field in bytes.
+ *  Exceeding bytes are trimmed.
+ * @param printable
+ *  Whether to limit the field contents to ASCII only.
+ * @param allow_slashes
+ *  Whether to allow slashes to be a part of input.
+ */
+#define HANDLE_INCOMING_STRING(tag, field, max_len, printable, allow_slashes) \
+    char *field = try_to_get_string(message, tag, max_len, printable, allow_slashes); \
+    if (field) \
+    { \
+        free(client->field); \
+        client->field = field; \
+        return; \
+    }
+
+    HANDLE_INCOMING_STRING("EXECUTABLE=", executable, PATH_MAX, true, true);
+    HANDLE_INCOMING_STRING("BACKTRACE=", backtrace, MAX_BACKTRACE_SIZE, false, true);
+    HANDLE_INCOMING_STRING("BASENAME=", basename, 100, true, false);
+    HANDLE_INCOMING_STRING("ANALYZER=", analyzer, 100, true, true);
+    HANDLE_INCOMING_STRING("REASON=", reason, 512, false, true);
+
+#undef HANDLE_INCOMING_STRING
+
+    /* PID is not handled as a string, we convert it to pid_t. */
+    if (starts_with(message, "PID="))
+    {
+        /* xatou() cannot be used here, because it would
+         * kill whole daemon by non-numeric string.
+         */
+        char *endptr;
+        errno = 0;
+        const char *nptr = message + strlen("PID=");
+        unsigned long number = strtoul(nptr, &endptr, 10);
+        /* pid == 0 is error, the lowest PID is 1. */
+        if (errno || nptr == endptr || *endptr != '\0' || number > UINT_MAX || number == 0)
+        {
+            error_msg("dumpsocket: invalid PID received -> ignoring");
+            return;
+        }
+        client->pid = number;
+        return;
+    }
+
+    /* Creates debug dump if all fields were already provided. */
+    if (starts_with(message, "DONE"))
+    {
+        if (client->pid == 0 ||
+            client->backtrace == NULL ||
+            client->executable == NULL ||
+            client->analyzer == NULL ||
+            client->basename == NULL ||
+            client->reason == NULL)
+        {
+            error_msg("dumpsocket: DONE received, but some data are missing -> ignoring");
+            return;
+        }
+
+        create_debug_dump(client);
+        return;
+    }
+}
+
+/* Callback called by glib main loop when ABRT receives data that have
+ * been written to the socket by some client.
+ */
+static gboolean client_socket_cb(GIOChannel *source,
+                                 GIOCondition condition,
+                                 gpointer data)
+{
+    struct client *client = (struct client*)data;
+
+    /* Detailed logging, useful for debugging. */
+    if (g_verbose >= 3)
+    {
+        char *cond = giocondition_to_string(condition);
+        log("dumpsocket: client condition %s", cond);
+        free(cond);
+    }
+
+    /* Handle incoming data. */
+    if (condition & (G_IO_IN | G_IO_PRI))
+    {
+        guint loop = client->messagebuf->len;
+        gsize len;
+        gchar buf[INPUT_BUFFER_SIZE];
+        GError *err = NULL;
+        /* Read data in chunks of size INPUT_BUFFER_SIZE. This allows to limit the number of
+           bytes received (to prevent memory exhaustion). */
+        do {
+            GIOStatus result = g_io_channel_read_chars(source, buf, INPUT_BUFFER_SIZE, &len, &err);
+            if (result == G_IO_STATUS_ERROR)
+            {
+                g_assert(err);
+                error_msg("dumpsocket: Error while reading data from client socket: %s", err->message);
+                g_error_free(err);
+                client_free(client);
+                return FALSE;
+            }
+
+            if (g_verbose >= 3)
+                log("dumpsocket: received %zu bytes of data", len);
+
+            /* Append the incoming data to the message buffer. */
+            g_byte_array_append(client->messagebuf, (guint8*)buf, len);
+
+            if (client->messagebuf->len > MAX_MESSAGE_SIZE)
+            {
+                error_msg("dumpsocket: Message too long.");
+                client_free(client);
+                return FALSE;
+            }
+        } while (len > 0);
+
+        /* Check, if we received a complete message now. */
+        for (; loop < client->messagebuf->len; ++loop)
+        {
+            if (client->messagebuf->data[loop] != '\0')
+                continue;
+
+            VERB3 log("dumpsocket: Processing message: %s",
+                      client->messagebuf->data);
+
+            /* Process the message. */
+            process_message(client, (char*)client->messagebuf->data);
+            /* Remove the message including the ending \0 */
+            g_byte_array_remove_range(client->messagebuf, 0, loop + 1);
+            loop = 0;
+        }
+
+        /* Update the last write access time */
+        client->lastwrite = time(NULL);
+    }
+
+    /* Handle socket disconnection.
+       It is important to do it after handling G_IO_IN, because sometimes
+       G_IO_HUP comes together with G_IO_IN. It means that some data arrived
+       and then the socket has been closed.
+     */
+    if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+    {
+        log("dumpsocket: Socket client disconnected");
+        client_free(client);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/* If the status indicates failure, report it. */
+static void check_status(GIOStatus status, GError *err, const char *operation)
+{
+    if (status == G_IO_STATUS_NORMAL)
+        return;
+
+    if (err)
+    {
+        error_msg("dumpsocket: Error while %s: %s", operation, err->message);
+        g_error_free(err);
+    }
+    else
+        error_msg("dumpsocket: Error while %s", operation);
+}
+
+/* Initializes a new client session data structure. */
+static struct client *client_new(int socket)
+{
+    struct client *client = (struct client*)xzalloc(sizeof(struct client));
+
+    /* Get credentials for the socket client. */
+    struct ucred cr;
+    socklen_t crlen = sizeof(struct ucred);
+    if (0 != getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &cr, &crlen))
+        perror_msg_and_die("dumpsocket: Failed to get client uid");
+    if (crlen != sizeof(struct ucred))
+        perror_msg_and_die("dumpsocket: Failed to get client uid (crlen)");
+    client->uid = cr.uid;
+
+    client->messagebuf = g_byte_array_new();
+    client->lastwrite = time(NULL);
+
+    close_on_exec_on(socket);
+
+    /* Create client IO channel. */
+    client->channel = g_io_channel_unix_new(socket);
+    g_io_channel_set_close_on_unref(client->channel, TRUE);
+
+    /* Set nonblocking access. */
+    GError *err = NULL;
+    GIOStatus status = g_io_channel_set_flags(client->channel, G_IO_FLAG_NONBLOCK, &err);
+    check_status(status, err, "setting NONBLOCK flag");
+
+    /* Disable channel encoding to protect binary data. */
+    err = NULL;
+    status = g_io_channel_set_encoding(client->channel, NULL, &err);
+    check_status(status, err, "setting encoding");
+
+    /* Start timer to check the client problems. */
+    client->timer_id = g_timeout_add_seconds(CLIENT_CHECK_INTERVAL, client_check_cb, client);
+    if (!client->timer_id)
+        error_msg_and_die("dumpsocket: Can't add client timer");
+
+    /* Register client callback. */
+    client->socket_id = g_io_add_watch(client->channel,
+                                       (GIOCondition)(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                                       (GIOFunc)client_socket_cb,
+                                       client);
+    if (!client->socket_id)
+        error_msg_and_die("dumpsocket: Can't add client socket watch");
+
+    ++client_count;
+    return client;
+}
+
+/* Callback called by glib main loop when a client newly opens ABRT's socket. */
+static gboolean server_socket_cb(GIOChannel *source,
+                                 GIOCondition condition,
+                                 gpointer data)
+{
+    /* Check the limit for number of simultaneously attached clients. */
+    if (client_count >= MAX_CLIENT_COUNT)
+    {
+        error_msg("dumpsocket: Too many clients, refusing connection.");
+        /* To avoid infinite loop caused by the descriptor in "ready" state,
+           the callback must be disabled.
+           It is added back in client_free(). */
+        g_source_remove(channel_cb_id);
+        channel_cb_id = 0;
+        return TRUE;
+    }
+
+    struct sockaddr_un remote;
+    socklen_t len = sizeof(remote);
+    int socket = accept(g_io_channel_unix_get_fd(source),
+                        (struct sockaddr*)&remote, &len);
+    if (socket == -1)
+    {
+        perror_msg("dumpsocket: Server can not accept client");
+        return TRUE;
+    }
+
+    log("dumpsocket: New client connected");
+    client_new(socket);
+    return TRUE;
+}
+
+/* Initializes the dump socket, usually in /var/run directory
+ * (the path depends on compile-time configuration).
+ */
+void dumpsocket_init()
+{
+    struct sockaddr_un local;
+    unlink(SOCKET_FILE); /* not caring about the result */
+    int socketfd = xsocket(AF_UNIX, SOCK_STREAM, 0);
+    close_on_exec_on(socketfd);
+    memset(&local, 0, sizeof(local));
+    local.sun_family = AF_UNIX;
+    strcpy(local.sun_path, SOCKET_FILE);
+    xbind(socketfd, (struct sockaddr*)&local, sizeof(local));
+    xlisten(socketfd, MAX_CLIENT_COUNT);
+
+    if (chmod(SOCKET_FILE, SOCKET_PERMISSION) != 0)
+        perror_msg_and_die("dumpsocket: failed to chmod socket file");
+
+    channel = g_io_channel_unix_new(socketfd);
+    g_io_channel_set_close_on_unref(channel, TRUE);
+    channel_cb_id = g_io_add_watch(channel,
+                                   (GIOCondition)(G_IO_IN | G_IO_PRI),
+                                   (GIOFunc)server_socket_cb,
+                                   NULL);
+    if (!channel_cb_id)
+        perror_msg_and_die("dumpsocket: Can't add socket watch");
+}
+
+/* Releases all resources used by dumpsocket. */
+void dumpsocket_shutdown()
+{
+    /* Set everything to pre-initialization state. */
+    if (channel)
+    {
+        /* This one is for g_io_add_watch. */
+        if (channel_cb_id)
+            g_source_remove(channel_cb_id);
+        /* This one is for g_io_channel_unix_new. */
+        g_io_channel_unref(channel);
+        channel = NULL;
+    }
+}
diff --git a/src/Daemon/dumpsocket.h b/src/Daemon/dumpsocket.h
new file mode 100644
index 0000000..e5b79d6
--- /dev/null
+++ b/src/Daemon/dumpsocket.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2010  ABRT team
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY 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, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef ABRT_DUMPSOCKET_H
+#define ABRT_DUMPSOCKET_H
+
+/*
+Unix socket in ABRT daemon for creating new dump directories.
+
+Why to use socket for creating dump dirs? Security. When a Python
+script throwns unexpected exception, ABRT handler catches it, running
+as a part of that broken Python application. The application is running
+with certain SELinux privileges, for example it can not execute other
+programs, or to create files in /var/cache or anything else required
+to properly fill a dump directory. Adding these privileges to every
+application would weaken the security.
+The most suitable solution is for the Python application
+to open a socket where ABRT daemon is listening, write all relevant
+data to that socket, and close it. ABRT daemon handles the rest.
+
+** Protocol
+
+Initializing new dump:
+open /var/run/abrt.socket
+
+Providing dump data (hook writes to the socket):
+-> "PID="
+   number 0 - PID_MAX (/proc/sys/kernel/pid_max)
+   \0
+-> "EXECUTABLE="
+   string (maximum length ~MAX_PATH)
+   \0
+-> "BACKTRACE="
+   string (maximum length 1 MB)
+   \0
+-> "ANALYZER="
+   string (maximum length 100 bytes)
+   \0
+-> "BASENAME="
+   string (maximum length 100 bytes, no slashes)
+   \0
+-> "REASON="
+   string (maximum length 512 bytes)
+   \0
+
+Finalizing dump creation:
+-> "DONE"
+   \0
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initializes the dump socket, usually in /var/run directory
+ * (the path depends on compile-time configuration).
+ */
+extern void dumpsocket_init();
+
+/* Releases all resources used by dumpsocket. */
+extern void dumpsocket_shutdown();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/Hooks/Makefile.am b/src/Hooks/Makefile.am
index ef4ab82..3c8aeb9 100644
--- a/src/Hooks/Makefile.am
+++ b/src/Hooks/Makefile.am
@@ -47,9 +47,10 @@ EXTRA_DIST = abrt_exception_handler.py.in $(man_MANS)
 
 CLEANFILES := $(notdir $(wildcard *~)) $(notdir $(wildcard *\#)) $(notdir $(wildcard \.\#*)) $(notdir $(wildcard *.pyc))
 
+# Must be synchronized with another sed call below.
 abrt_exception_handler.py:
-	sed s,@DEBUG_DUMP_DIR@,$(DEBUG_DUMPS_DIR),g abrt_exception_handler.py.in  > abrt_exception_handler.py
+	sed s,\@VAR_RUN\@,\"$(VAR_RUN)\",g abrt_exception_handler.py.in > abrt_exception_handler.py
 
 # RPM fix: we need to regenerate abrt_exception_handler.py, because it has the default ddir
 install-data-local:
-	sed s,@DEBUG_DUMP_DIR@,$(DEBUG_DUMPS_DIR),g abrt_exception_handler.py.in  > abrt_exception_handler.py
+	sed s,\@VAR_RUN\@,\"$(VAR_RUN)\",g abrt_exception_handler.py.in > abrt_exception_handler.py
diff --git a/src/Hooks/abrt_exception_handler.py.in b/src/Hooks/abrt_exception_handler.py.in
index a4bc93d..9981729 100644
--- a/src/Hooks/abrt_exception_handler.py.in
+++ b/src/Hooks/abrt_exception_handler.py.in
@@ -19,27 +19,34 @@
 ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 """
-Module for a userfriendly exception handling
+Module for the ABRT exception handling hook
 """
 
 import sys
 import os
 import syslog
 import subprocess
+import socket
 
 def write_dump(pid, tb):
     executable = "Exception raised from python shell"
     if sys.argv[0]:
         executable = os.path.abspath(sys.argv[0])
 
-    command = ["/usr/libexec/abrt-hook-python"]
-    command.append("--pid=%s" % pid)
-    command.append("--executable=%s" % executable)
-
-    helper = subprocess.Popen(command, stdin=subprocess.PIPE)
-    helper.communicate(tb)
-    helper.wait()
-
+    # Open ABRT daemon's socket and write data to it.
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    s.connect(@VAR_RUN@ + "/abrt.socket")
+    s.sendall("PID=%s\0" % pid)
+    s.sendall("EXECUTABLE=%s\0" % executable)
+    s.sendall("ANALYZER=Python\0")
+    s.sendall("BASENAME=pyhook\0")
+    # This handler puts a short(er) crash descr in 1st line of the backtrace.
+    # Example:
+    # CCMainWindow.py:1:<module>:ZeroDivisionError: integer division or modulo by zero
+    s.sendall("REASON=%s\0" % tb.splitlines()[0])
+    s.sendall("BACKTRACE=%s\0" % tb)
+    s.sendall("DONE\0")
+    s.close()
 
 def handleMyException((etype, value, tb)):
     """
-- 
1.6.6.1

