>From 66b8ce6fc967c93f81fdb0ae28fb5dbac0ea2373 Mon Sep 17 00:00:00 2001
From: Karel Klic <kklic@redhat.com>
Date: Tue, 4 May 2010 19:02:57 +0200
Subject: [PATCH 2/3] Daemon socket for reporting crashes

---
 src/Daemon/Daemon.cpp                  |    5 +
 src/Daemon/Makefile.am                 |    3 +-
 src/Daemon/dumpsocket.cpp              |  528 ++++++++++++++++++++++++++++++++
 src/Daemon/dumpsocket.h                |   82 +++++
 src/Hooks/Makefile.am                  |    5 +-
 src/Hooks/abrt_exception_handler.py.in |   26 +-
 6 files changed, 637 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 57e560e..9982930 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;
 
@@ -849,6 +850,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.
          */
@@ -900,6 +904,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 c2347ae..fbcbab3 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..8f79005
--- /dev/null
+++ b/src/Daemon/dumpsocket.cpp
@@ -0,0 +1,528 @@
+/*
+  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
+
+#define MAX_BACKTRACE_SIZE (1024*1024)
+
+static int socketfd = -1;
+static GIOChannel *channel = NULL;
+
+/* 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;
+};
+
+/* Initializes a new client session data structure. */
+static struct client *client_new(uid_t uid)
+{
+    struct client *client = (struct client*)xmalloc(sizeof(struct client));
+    client->uid = uid;
+    client->messagebuf = g_byte_array_new();
+    client->executable = NULL;
+    client->pid = 0;
+    client->backtrace = NULL;
+    client->analyzer = NULL;
+    client->basename = NULL;
+    client->reason = NULL;
+    return client;
+}
+
+/* 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);
+    free(client);
+}
+
+/* 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[PATH_MAX];
+    unsigned path_len = snprintf(path,
+                                 sizeof(path),
+                                 DEBUG_DUMPS_DIR"/%s-%ld-%u.new",
+                                 client->basename,
+                                 (long)time(NULL),
+                                 client->pid);
+    if (path_len >= sizeof(path))
+    {
+        error_msg("dumpsocket: Dump path too long -> ignoring dump");
+        return;
+    }
+
+    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, path_len - 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);
+    if (maxCrashReportsSize > 0)
+    {
+        check_free_space(maxCrashReportsSize);
+        trim_debug_dumps(maxCrashReportsSize, 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() to 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)
+{
+    if (!starts_with(message, tag))
+        return NULL;
+
+    const char *contents = message + strlen(tag);
+    if (printable && !printable_str(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)
+{
+    if (starts_with(message, "NEW"))
+    {
+        /* Clear all fields. */
+        free(client->executable);
+        client->executable = NULL;
+        free(client->backtrace);
+        client->backtrace = NULL;
+        free(client->analyzer);
+        client->analyzer = NULL;
+        free(client->basename);
+        client->basename = NULL;
+        free(client->reason);
+        client->reason = NULL;
+        client->pid = 0;
+        return;
+    }
+
+/* @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.
+ */
+#define HANDLE_INCOMING_STRING(__tag, __field, __max_len, __printable) \
+    char *__field = try_to_get_string(message, __tag, __max_len,  __printable); \
+    if (__field) \
+    { \
+        free(client->__field); \
+        client->__field = __field; \
+        return; \
+    }
+
+    HANDLE_INCOMING_STRING("EXECUTABLE=", executable, PATH_MAX, true);
+    HANDLE_INCOMING_STRING("BACKTRACE=", backtrace, MAX_BACKTRACE_SIZE, false);
+    HANDLE_INCOMING_STRING("ANALYZER=", analyzer, 100, true);
+    HANDLE_INCOMING_STRING("BASENAME=", basename, 100, true);
+    HANDLE_INCOMING_STRING("REASON=", reason, 512, false);
+
+#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;
+        int old_errno = errno;
+        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;
+        }
+        errno = old_errno;
+        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;
+    }
+}
+
+/* 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;
+}
+
+/* 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))
+    {
+        gsize len = 0;
+        guint8 *buf = NULL;
+        GError *err = NULL;
+        GIOStatus result = g_io_channel_read_to_end(source, (gchar**)&buf,
+                                                    &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);
+            return FALSE;
+        }
+
+        if (!buf)
+            return TRUE;
+
+        guint loop = client->messagebuf->len;
+        /* Append the incoming data to the message buffer and check, if we have
+           a complete message now. */
+        g_byte_array_append(client->messagebuf, buf, len);
+        for (; loop < client->messagebuf->len; ++loop)
+        {
+            if (client->messagebuf->data[loop] != '\0')
+                continue;
+
+            VERB3 log("dumpsocket: Processing message: %s",
+                      client->messagebuf->data);
+
+            /* Process the complete 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;
+        }
+        g_free(buf);
+    }
+
+    /* 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);
+        /* g_io_add_watch call incremented channel's reference count,
+           so unref this one now. */
+        g_io_channel_unref(source);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/* 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)
+{
+    int socket;
+    struct sockaddr_un remote;
+    socklen_t len = sizeof(remote);
+
+    if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+    {
+        error_msg("dumpsocket: Server socket error");
+        return FALSE;
+    }
+
+    if ((socket = accept(socketfd, (struct sockaddr*)&remote, &len)) == -1)
+    {
+        error_msg("dumpsocket: Server can not accept client");
+        return TRUE;
+    }
+
+    log("dumpsocket: New client connected");
+
+    GIOChannel* gsocket = g_io_channel_unix_new(socket);
+
+    /* Disable channel encoding to protect binary data. */
+    GError *err = NULL;
+    GIOStatus status = g_io_channel_set_encoding(gsocket, NULL, &err);
+    if (status != G_IO_STATUS_NORMAL)
+    {
+        if (err)
+        {
+            error_msg("dumpsocket: Error while setting encoding: %s", err->message);
+            g_error_free(err);
+        }
+        else
+            error_msg("dumpsocket: Error while setting encoding");
+    }
+
+    /* Get credentials for the newly attached 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)");
+
+    /* Register client callback.
+     * Provide a messsage buffer to store incoming data there.
+     */
+    struct client *client = client_new(cr.uid);
+    if (!g_io_add_watch(gsocket,
+                        (GIOCondition)(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                        (GIOFunc)client_socket_cb,
+                        client))
+    {
+        error_msg("dumpsocket: Can't add client socket watch");
+        return TRUE;
+    }
+    return TRUE;
+}
+
+/* Set the FD_CLOEXEC flag of desc if value is nonzero,
+   or clear the flag if value is 0.
+   Return 0 on success, or -1 on error with errno set.
+   Copied from GNU C Library Manual.
+*/
+static int set_cloexec_flag(int desc, int value)
+{
+    int oldflags = fcntl(desc, F_GETFD, 0);
+    /* If reading the flags failed, return error indication now. */
+    if (oldflags < 0)
+        return oldflags;
+    /* Set just the flag we want to set. */
+    if (value != 0)
+        oldflags |= FD_CLOEXEC;
+    else
+        oldflags &= ~FD_CLOEXEC;
+    /* Store modified flag word in the descriptor. */
+    return fcntl(desc, F_SETFD, oldflags);
+}
+
+/* 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);
+    socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (socketfd == -1)
+        perror_msg_and_die("dumpsocket: Can't create AF_UNIX socket");
+
+    if (set_cloexec_flag(socketfd, 1) != 0)
+        perror_msg_and_die("dumpsocket: Failed to set CLOEXEC flag.");
+
+    memset(&local, 0, sizeof(local));
+    local.sun_family = AF_UNIX;
+    strcpy(local.sun_path, SOCKET_FILE);
+    if (bind(socketfd, (struct sockaddr*)&local, sizeof(local)) == -1)
+        perror_msg_and_die("dumpsocket: Can't bind AF_UNIX socket to '%s'", SOCKET_FILE);
+
+    if (listen(socketfd, 10) != 0)
+        perror_msg_and_die("dumpsocket: Can't listen on AF_UNIX socket");
+
+    if (chmod(SOCKET_FILE, SOCKET_PERMISSION) != 0)
+        perror_msg_and_die("dumpsocket: failed to chmod socket file");
+
+    channel = g_io_channel_unix_new(socketfd);
+    if (!g_io_add_watch(channel,
+                        (GIOCondition)(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                        (GIOFunc)server_socket_cb,
+                        NULL))
+    {
+        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)
+    {
+        g_io_channel_unref(channel);
+        channel = NULL;
+    }
+    if (socketfd != -1)
+    {
+        close(socketfd);
+        socketfd = -1;
+    }
+}
diff --git a/src/Daemon/dumpsocket.h b/src/Daemon/dumpsocket.h
new file mode 100644
index 0000000..1c400d1
--- /dev/null
+++ b/src/Daemon/dumpsocket.h
@@ -0,0 +1,82 @@
+/*
+  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:
+-> "NEW"
+   \0
+
+Providing dump data:
+-> "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)
+   \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 8a5d806..e4c97a9 100644
--- a/src/Hooks/abrt_exception_handler.py.in
+++ b/src/Hooks/abrt_exception_handler.py.in
@@ -19,13 +19,14 @@
 ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 """
-Module for a userfriendly exception handling
+Module for a 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"
@@ -35,14 +36,21 @@ def write_dump(pid, tb):
         # during execution?
         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("NEW\0")
+    s.sendall("PID=%s\0" % pid)
+    s.sendall("EXECUTABLE=%s\0" % executable)
+    s.sendall("BACKTRACE=%s\0" % tb)
+    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("DONE\0")
+    s.close()
 
 def handleMyException((etype, value, tb)):
     """
-- 
1.6.6.1

