Blame src/daemon/abrt-server.c

Packit Service 8a8a03
/*
Packit Service 8a8a03
  Copyright (C) 2010  ABRT team
Packit Service 8a8a03
Packit Service 8a8a03
  This program is free software; you can redistribute it and/or modify
Packit Service 8a8a03
  it under the terms of the GNU General Public License as published by
Packit Service 8a8a03
  the Free Software Foundation; either version 2 of the License, or
Packit Service 8a8a03
  (at your option) any later version.
Packit Service 8a8a03
Packit Service 8a8a03
  This program is distributed in the hope that it will be useful,
Packit Service 8a8a03
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 8a8a03
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 8a8a03
  GNU General Public License for more details.
Packit Service 8a8a03
Packit Service 8a8a03
  You should have received a copy of the GNU General Public License along
Packit Service 8a8a03
  with this program; if not, write to the Free Software Foundation, Inc.,
Packit Service 8a8a03
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service 8a8a03
*/
Packit Service 8a8a03
#include "problem_api.h"
Packit Service 8a8a03
#include "abrt_glib.h"
Packit Service 8a8a03
#include "libabrt.h"
Packit Service 8a8a03
Packit Service 8a8a03
/* Maximal length of backtrace. */
Packit Service 8a8a03
#define MAX_BACKTRACE_SIZE (1024*1024)
Packit Service 8a8a03
/* Amount of data received from one client for a message before reporting error. */
Packit Service 8a8a03
#define MAX_MESSAGE_SIZE (4*MAX_BACKTRACE_SIZE)
Packit Service 8a8a03
/* Maximal number of characters read from socket at once. */
Packit Service 8a8a03
#define INPUT_BUFFER_SIZE (8*1024)
Packit Service 8a8a03
/* We exit after this many seconds */
Packit Service 8a8a03
#define TIMEOUT 10
Packit Service 8a8a03
Packit Service 8a8a03
#define ABRT_SERVER_EVENT_ENV "ABRT_SERVER_PID"
Packit Service 8a8a03
Packit Service 8a8a03
/*
Packit Service 8a8a03
Unix socket in ABRT daemon for creating new dump directories.
Packit Service 8a8a03
Packit Service 8a8a03
Why to use socket for creating dump dirs? Security. When a Python
Packit Service 8a8a03
script throws unexpected exception, ABRT handler catches it, running
Packit Service 8a8a03
as a part of that broken Python application. The application is running
Packit Service 8a8a03
with certain SELinux privileges, for example it can not execute other
Packit Service 8a8a03
programs, or to create files in /var/cache or anything else required
Packit Service 8a8a03
to properly fill a problem directory. Adding these privileges to every
Packit Service 8a8a03
application would weaken the security.
Packit Service 8a8a03
The most suitable solution is for the Python application
Packit Service 8a8a03
to open a socket where ABRT daemon is listening, write all relevant
Packit Service 8a8a03
data to that socket, and close it. ABRT daemon handles the rest.
Packit Service 8a8a03
Packit Service 8a8a03
** Protocol
Packit Service 8a8a03
Packit Service 8a8a03
Initializing new dump:
Packit Service 8a8a03
open /var/run/abrt.socket
Packit Service 8a8a03
Packit Service 8a8a03
Providing dump data (hook writes to the socket):
Packit Service 8a8a03
MANDATORY ITEMS:
Packit Service 8a8a03
-> "PID="
Packit Service 8a8a03
   number 0 - PID_MAX (/proc/sys/kernel/pid_max)
Packit Service 8a8a03
   \0
Packit Service 8a8a03
-> "EXECUTABLE="
Packit Service 8a8a03
   string
Packit Service 8a8a03
   \0
Packit Service 8a8a03
-> "BACKTRACE="
Packit Service 8a8a03
   string
Packit Service 8a8a03
   \0
Packit Service 8a8a03
-> "ANALYZER="
Packit Service 8a8a03
   string
Packit Service 8a8a03
   \0
Packit Service 8a8a03
-> "BASENAME="
Packit Service 8a8a03
   string (no slashes)
Packit Service 8a8a03
   \0
Packit Service 8a8a03
-> "REASON="
Packit Service 8a8a03
   string
Packit Service 8a8a03
   \0
Packit Service 8a8a03
Packit Service 8a8a03
You can send more messages using the same KEY=value format.
Packit Service 8a8a03
*/
Packit Service 8a8a03
Packit Service 8a8a03
static int g_signal_pipe[2];
Packit Service 8a8a03
static struct ns_ids g_ns_ids;
Packit Service 8a8a03
Packit Service 8a8a03
struct waiting_context
Packit Service 8a8a03
{
Packit Service 8a8a03
    GMainLoop *main_loop;
Packit Service 8a8a03
    const char *dirname;
Packit Service 8a8a03
    int retcode;
Packit Service 8a8a03
    enum abrt_daemon_reply
Packit Service 8a8a03
    {
Packit Service 8a8a03
        ABRT_CONTINUE,
Packit Service 8a8a03
        ABRT_INTERRUPT,
Packit Service 8a8a03
    } reply;
Packit Service 8a8a03
};
Packit Service 8a8a03
Packit Service 8a8a03
static unsigned total_bytes_read = 0;
Packit Service 8a8a03
Packit Service 8a8a03
static pid_t client_pid = (pid_t)-1L;
Packit Service 8a8a03
static uid_t client_uid = (uid_t)-1L;
Packit Service 8a8a03
Packit Service 8a8a03
static void
Packit Service 8a8a03
handle_signal(int signo)
Packit Service 8a8a03
{
Packit Service 8a8a03
    int save_errno = errno;
Packit Service 8a8a03
    uint8_t sig_caught = signo;
Packit Service 8a8a03
    if (write(g_signal_pipe[1], &sig_caught, 1))
Packit Service 8a8a03
        /* we ignore result, if () shuts up stupid compiler */;
Packit Service 8a8a03
    errno = save_errno;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static gboolean
Packit Service 8a8a03
handle_signal_pipe_cb(GIOChannel *gio, GIOCondition condition, gpointer user_data)
Packit Service 8a8a03
{
Packit Service 8a8a03
    gsize len = 0;
Packit Service 8a8a03
    uint8_t signals[2];
Packit Service 8a8a03
Packit Service 8a8a03
    for (;;)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        GError *error = NULL;
Packit Service 8a8a03
        GIOStatus stat = g_io_channel_read_chars(gio, (void *)signals, sizeof(signals), &len, NULL);
Packit Service 8a8a03
        if (stat == G_IO_STATUS_ERROR)
Packit Service 8a8a03
            error_msg_and_die(_("Can't read from gio channel: '%s'"), error ? error->message : "");
Packit Service 8a8a03
        if (stat == G_IO_STATUS_EOF)
Packit Service 8a8a03
            return FALSE; /* Remove this GLib source */
Packit Service 8a8a03
        if (stat == G_IO_STATUS_AGAIN)
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        /* G_IO_STATUS_NORMAL */
Packit Service 8a8a03
        for (unsigned signo = 0; signo < len; ++signo)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            /* we did receive a signal */
Packit Service 8a8a03
            struct waiting_context *context = (struct waiting_context *)user_data;
Packit Service 8a8a03
            log_debug("Got signal %d through signal pipe", signals[signo]);
Packit Service 8a8a03
            switch (signals[signo])
Packit Service 8a8a03
            {
Packit Service 8a8a03
                case SIGUSR1: context->reply = ABRT_CONTINUE; break;
Packit Service 8a8a03
                case SIGINT:  context->reply = ABRT_INTERRUPT; break;
Packit Service 8a8a03
                default:
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    error_msg("Bug - aborting - unsupported signal: %d", signals[signo]);
Packit Service 8a8a03
                    abort();
Packit Service 8a8a03
                }
Packit Service 8a8a03
            }
Packit Service 8a8a03
Packit Service 8a8a03
            g_main_loop_quit(context->main_loop);
Packit Service 8a8a03
            return FALSE; /* remove this event */
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return TRUE; /* "please don't remove this event" */
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
/* Remove dump dir */
Packit Service 8a8a03
static int delete_path(const char *dump_dir_name)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* If doesn't start with "g_settings_dump_location/"... */
Packit Service 8a8a03
    if (!dir_is_in_dump_location(dump_dir_name))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        /* Then refuse to operate on it (someone is attacking us??) */
Packit Service 8a8a03
        error_msg("Bad problem directory name '%s', should start with: '%s'", dump_dir_name, g_settings_dump_location);
Packit Service 8a8a03
        return 400; /* Bad Request */
Packit Service 8a8a03
    }
Packit Service 8a8a03
    if (!dir_has_correct_permissions(dump_dir_name, DD_PERM_DAEMONS))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        error_msg("Problem directory '%s' has wrong owner or group", dump_dir_name);
Packit Service 8a8a03
        return 400; /*  */
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_FD_ONLY);
Packit Service 8a8a03
    if (dd == NULL)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        perror_msg("Can't open problem directory '%s'", dump_dir_name);
Packit Service 8a8a03
        return 400;
Packit Service 8a8a03
    }
Packit Service 8a8a03
    if (!dd_accessible_by_uid(dd, client_uid))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        dd_close(dd);
Packit Service 8a8a03
        if (errno == ENOTDIR)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("Path '%s' isn't problem directory", dump_dir_name);
Packit Service 8a8a03
            return 404; /* Not Found */
Packit Service 8a8a03
        }
Packit Service 8a8a03
        error_msg("Problem directory '%s' can't be accessed by user with uid %ld", dump_dir_name, (long)client_uid);
Packit Service 8a8a03
        return 403; /* Forbidden */
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    dd = dd_fdopendir(dd, /*flags:*/ 0);
Packit Service 8a8a03
    if (dd)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (dd_delete(dd) != 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("Failed to delete problem directory '%s'", dump_dir_name);
Packit Service 8a8a03
            dd_close(dd);
Packit Service 8a8a03
            return 400;
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return 0; /* success */
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *event_name, int *fdp)
Packit Service 8a8a03
{
Packit Service 8a8a03
    char *args[9];
Packit Service 8a8a03
    args[0] = (char *) LIBEXEC_DIR"/abrt-handle-event";
Packit Service 8a8a03
    /* Do not forward ASK_* messages to parent*/
Packit Service 8a8a03
    args[1] = (char *) "-i";
Packit Service 8a8a03
    args[2] = (char *) "--nice";
Packit Service 8a8a03
    args[3] = (char *) "10";
Packit Service 8a8a03
    args[4] = (char *) "-e";
Packit Service 8a8a03
    args[5] = (char *) event_name;
Packit Service 8a8a03
    args[6] = (char *) "--";
Packit Service 8a8a03
    args[7] = (char *) dump_dir_name;
Packit Service 8a8a03
    args[8] = NULL;
Packit Service 8a8a03
Packit Service 8a8a03
    int pipeout[2];
Packit Service 8a8a03
    int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | EXECFLG_ERR2OUT;
Packit Service 8a8a03
    VERB1 flags &= ~EXECFLG_QUIET;
Packit Service 8a8a03
Packit Service 8a8a03
    char *env_vec[3];
Packit Service 8a8a03
    /* Intercept ASK_* messages in Client API -> don't wait for user response */
Packit Service 8a8a03
    env_vec[0] = xstrdup("REPORT_CLIENT_NONINTERACTIVE=1");
Packit Service 8a8a03
    env_vec[1] = xasprintf("%s=%d", ABRT_SERVER_EVENT_ENV, getpid());
Packit Service 8a8a03
    env_vec[2] = NULL;
Packit Service 8a8a03
Packit Service 8a8a03
    pid_t child = fork_execv_on_steroids(flags, args, pipeout,
Packit Service 8a8a03
                                         env_vec, /*dir:*/ NULL,
Packit Service 8a8a03
                                         /*uid(unused):*/ 0);
Packit Service 8a8a03
    if (fdp)
Packit Service 8a8a03
        *fdp = pipeout[0];
Packit Service 8a8a03
    return child;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static int problem_dump_dir_was_provoked_by_abrt_event(struct dump_dir *dd, char  **provoker)
Packit Service 8a8a03
{
Packit Service 8a8a03
    char *env_var = NULL;
Packit Service 8a8a03
    const int r = dd_get_env_variable(dd, ABRT_SERVER_EVENT_ENV, &env_var);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Dump directory doesn't contain the environ file */
Packit Service 8a8a03
    if (r == -ENOENT)
Packit Service 8a8a03
        return 0;
Packit Service 8a8a03
Packit Service 8a8a03
    if (provoker != NULL)
Packit Service 8a8a03
        *provoker = env_var;
Packit Service 8a8a03
    else
Packit Service 8a8a03
        free(env_var);
Packit Service 8a8a03
Packit Service 8a8a03
    return env_var != NULL;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static int
Packit Service 8a8a03
emit_new_problem_signal(gpointer data)
Packit Service 8a8a03
{
Packit Service 8a8a03
    struct waiting_context *context = (struct waiting_context *)data;
Packit Service 8a8a03
Packit Service 8a8a03
    const size_t wrote = fprintf(stderr, "NEW_PROBLEM_DETECTED: %s\n", context->dirname);
Packit Service 8a8a03
    fflush(stderr);
Packit Service 8a8a03
Packit Service 8a8a03
    if (wrote <= 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        error_msg("Failed to communicate with the daemon");
Packit Service 8a8a03
        context->retcode = 503;
Packit Service 8a8a03
        g_main_loop_quit(context->main_loop);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    log_notice("Emitted new problem signal, waiting for SIGUSR1|SIGINT");
Packit Service 8a8a03
    return FALSE;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
struct response
Packit Service 8a8a03
{
Packit Service 8a8a03
    int code;
Packit Service 8a8a03
    char *message;
Packit Service 8a8a03
};
Packit Service 8a8a03
Packit Service 8a8a03
#define RESPONSE_SETTER(r, c, m) \
Packit Service 8a8a03
    do { if (r != NULL) { r->message = m; r->code = c; } else { free(m); }} while (0)
Packit Service 8a8a03
Packit Service 8a8a03
#define RESPONSE_RETURN(r, c ,m) \
Packit Service 8a8a03
    do { RESPONSE_SETTER(r, c, m); \
Packit Service 8a8a03
         return c; } while (0)
Packit Service 8a8a03
Packit Service 8a8a03
Packit Service 8a8a03
static int run_post_create(const char *dirname, struct response *resp)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* If doesn't start with "g_settings_dump_location/"... */
Packit Service 8a8a03
    if (!dir_is_in_dump_location(dirname))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        /* Then refuse to operate on it (someone is attacking us??) */
Packit Service 8a8a03
        error_msg("Bad problem directory name '%s', should start with: '%s'", dirname, g_settings_dump_location);
Packit Service 8a8a03
        RESPONSE_RETURN(resp, 400, NULL);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    if (!dir_has_correct_permissions(dirname, DD_PERM_EVENTS))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        error_msg("Problem directory '%s' has wrong owner or group", dirname);
Packit Service 8a8a03
        RESPONSE_RETURN(resp, 400, NULL);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    /* Check completness */
Packit Service 8a8a03
    {
Packit Service 8a8a03
        struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY);
Packit Service 8a8a03
Packit Service 8a8a03
        char *provoker = NULL;
Packit Service 8a8a03
        const bool event_dir = dd && problem_dump_dir_was_provoked_by_abrt_event(dd, &provoker);
Packit Service 8a8a03
        if (event_dir)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (g_settings_debug_level == 0)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                error_msg("Removing problem provoked by ABRT(pid:%s): '%s'", provoker, dirname);
Packit Service 8a8a03
                dd_delete(dd);
Packit Service 8a8a03
            }
Packit Service 8a8a03
            else
Packit Service 8a8a03
            {
Packit Service 8a8a03
                char *dumpdir = NULL;
Packit Service 8a8a03
                char *event   = NULL;
Packit Service 8a8a03
                char *reason  = NULL;
Packit Service 8a8a03
                char *cmdline = NULL;
Packit Service 8a8a03
Packit Service 8a8a03
                /* Ignore errors */
Packit Service 8a8a03
                dd_get_env_variable(dd, "DUMP_DIR", &dumpdir);
Packit Service 8a8a03
                dd_get_env_variable(dd, "EVENT",    &event);
Packit Service 8a8a03
                reason  = dd_load_text(dd, FILENAME_REASON);
Packit Service 8a8a03
                cmdline = dd_load_text(dd, FILENAME_CMDLINE);
Packit Service 8a8a03
Packit Service 8a8a03
                error_msg("ABRT_SERVER_PID=%s;DUMP_DIR='%s';EVENT='%s';REASON='%s';CMDLINE='%s'",
Packit Service 8a8a03
                           provoker, dumpdir, event, reason, cmdline);
Packit Service 8a8a03
Packit Service 8a8a03
            }
Packit Service 8a8a03
Packit Service 8a8a03
            free(provoker);
Packit Service 8a8a03
            return 400;
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        const bool complete = dd && problem_dump_dir_is_complete(dd);
Packit Service 8a8a03
        dd_close(dd);
Packit Service 8a8a03
        if (complete)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("Problem directory '%s' has already been processed", dirname);
Packit Service 8a8a03
            RESPONSE_RETURN(resp, 403, NULL);
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /*
Packit Service 8a8a03
     * The post-create event cannot be run concurrently for more problem
Packit Service 8a8a03
     * directories. The problem is in searching for duplicates process
Packit Service 8a8a03
     * in case when two concurrently processed directories are duplicates
Packit Service 8a8a03
     * of each other. Both of the directories are marked as duplicates
Packit Service 8a8a03
     * of each other and are deleted.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    log_debug("Creating glib main loop");
Packit Service 8a8a03
    struct waiting_context context = {0};
Packit Service 8a8a03
    context.main_loop = g_main_loop_new(NULL, FALSE);
Packit Service 8a8a03
    context.dirname = strrchr(dirname, '/') + 1;
Packit Service 8a8a03
Packit Service 8a8a03
    log_debug("Setting up a signal handler");
Packit Service 8a8a03
    /* Set up signal pipe */
Packit Service 8a8a03
    xpipe(g_signal_pipe);
Packit Service 8a8a03
    close_on_exec_on(g_signal_pipe[0]);
Packit Service 8a8a03
    close_on_exec_on(g_signal_pipe[1]);
Packit Service 8a8a03
    ndelay_on(g_signal_pipe[0]);
Packit Service 8a8a03
    ndelay_on(g_signal_pipe[1]);
Packit Service 8a8a03
    signal(SIGUSR1, handle_signal);
Packit Service 8a8a03
    signal(SIGINT, handle_signal);
Packit Service 8a8a03
    GIOChannel *channel_signal = abrt_gio_channel_unix_new(g_signal_pipe[0]);
Packit Service 8a8a03
    g_io_add_watch(channel_signal, G_IO_IN | G_IO_PRI, handle_signal_pipe_cb, &context);
Packit Service 8a8a03
Packit Service 8a8a03
    g_idle_add(emit_new_problem_signal, &context);
Packit Service 8a8a03
Packit Service 8a8a03
    g_main_loop_run(context.main_loop);
Packit Service 8a8a03
Packit Service 8a8a03
    g_main_loop_unref(context.main_loop);
Packit Service 8a8a03
    g_io_channel_unref(channel_signal);
Packit Service 8a8a03
    close(g_signal_pipe[1]);
Packit Service 8a8a03
Packit Service 8a8a03
    log_notice("Waiting finished");
Packit Service 8a8a03
Packit Service 8a8a03
    if (context.retcode != 0)
Packit Service 8a8a03
        RESPONSE_RETURN(resp, context.retcode, NULL);
Packit Service 8a8a03
Packit Service 8a8a03
    if (context.reply != ABRT_CONTINUE)
Packit Service 8a8a03
        /* The only reason for the interruption is removed problem directory */
Packit Service 8a8a03
        RESPONSE_RETURN(resp, 413, NULL);
Packit Service 8a8a03
    /*
Packit Service 8a8a03
     * The post-create event synchronization done.
Packit Service 8a8a03
     */
Packit Service 8a8a03
Packit Service 8a8a03
    int child_stdout_fd;
Packit Service 8a8a03
    int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd);
Packit Service 8a8a03
Packit Service 8a8a03
    char *dup_of_dir = NULL;
Packit Service 8a8a03
    struct strbuf *cmd_output = strbuf_new();
Packit Service 8a8a03
Packit Service 8a8a03
    bool child_is_post_create = 1; /* else it is a notify child */
Packit Service 8a8a03
Packit Service 8a8a03
 read_child_output:
Packit Service 8a8a03
    //log_warning("Reading from event fd %d", child_stdout_fd);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Read streamed data and split lines */
Packit Service 8a8a03
    for (;;)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        char buf[250]; /* usually we get one line, no need to have big buf */
Packit Service 8a8a03
        errno = 0;
Packit Service 8a8a03
        int r = safe_read(child_stdout_fd, buf, sizeof(buf) - 1);
Packit Service 8a8a03
        if (r <= 0)
Packit Service 8a8a03
            break;
Packit Service 8a8a03
        buf[r] = '\0';
Packit Service 8a8a03
Packit Service 8a8a03
        /* split lines in the current buffer */
Packit Service 8a8a03
        char *raw = buf;
Packit Service 8a8a03
        char *newline;
Packit Service 8a8a03
        while ((newline = strchr(raw, '\n')) != NULL)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            *newline = '\0';
Packit Service 8a8a03
            strbuf_append_str(cmd_output, raw);
Packit Service 8a8a03
            char *msg = cmd_output->buf;
Packit Service 8a8a03
Packit Service 8a8a03
            if (child_is_post_create
Packit Service 8a8a03
             && prefixcmp(msg, "DUP_OF_DIR: ") == 0
Packit Service 8a8a03
            ) {
Packit Service 8a8a03
                free(dup_of_dir);
Packit Service 8a8a03
                dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: "));
Packit Service 8a8a03
            }
Packit Service 8a8a03
            else
Packit Service 8a8a03
                log_warning("%s", msg);
Packit Service 8a8a03
Packit Service 8a8a03
            strbuf_clear(cmd_output);
Packit Service 8a8a03
            /* jump to next line */
Packit Service 8a8a03
            raw = newline + 1;
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        /* beginning of next line. the line continues by next read */
Packit Service 8a8a03
        strbuf_append_str(cmd_output, raw);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* EOF/error */
Packit Service 8a8a03
Packit Service 8a8a03
    /* Wait for child to actually exit, collect status */
Packit Service 8a8a03
    int status = 0;
Packit Service 8a8a03
    if (safe_waitpid(child_pid, &status, 0) <= 0)
Packit Service 8a8a03
    /* should not happen */
Packit Service 8a8a03
        perror_msg("waitpid(%d)", child_pid);
Packit Service 8a8a03
Packit Service 8a8a03
    /* If it was a "notify[-dup]" event, then we're done */
Packit Service 8a8a03
    if (!child_is_post_create)
Packit Service 8a8a03
        goto ret;
Packit Service 8a8a03
Packit Service 8a8a03
    /* exit 0 means "this is a good, non-dup dir" */
Packit Service 8a8a03
    /* exit with 1 + "DUP_OF_DIR: dir" string => dup */
Packit Service 8a8a03
    if (status != 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (WIFSIGNALED(status))
Packit Service 8a8a03
        {
Packit Service 8a8a03
            log_warning("'post-create' on '%s' killed by signal %d",
Packit Service 8a8a03
                            dirname, WTERMSIG(status));
Packit Service 8a8a03
            goto delete_bad_dir;
Packit Service 8a8a03
        }
Packit Service 8a8a03
        /* else: it is WIFEXITED(status) */
Packit Service 8a8a03
        if (!dup_of_dir)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            log_warning("'post-create' on '%s' exited with %d",
Packit Service 8a8a03
                            dirname, WEXITSTATUS(status));
Packit Service 8a8a03
            goto delete_bad_dir;
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    const char *work_dir = (dup_of_dir ? dup_of_dir : dirname);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Load problem_data (from the *first dir* if this one is a dup) */
Packit Service 8a8a03
    struct dump_dir *dd = dd_opendir(work_dir, /*flags:*/ 0);
Packit Service 8a8a03
    if (!dd)
Packit Service 8a8a03
        /* dd_opendir already emitted error msg */
Packit Service 8a8a03
        goto delete_bad_dir;
Packit Service 8a8a03
Packit Service 8a8a03
    /* Update count */
Packit Service 8a8a03
    char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT);
Packit Service 8a8a03
    unsigned long count = strtoul(count_str, NULL, 10);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Don't increase crash count if we are working with newly uploaded
Packit Service 8a8a03
     * directory (remote crash) which already has its crash count set.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    if ((status != 0 && dup_of_dir) || count == 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        count++;
Packit Service 8a8a03
        char new_count_str[sizeof(long)*3 + 2];
Packit Service 8a8a03
        sprintf(new_count_str, "%lu", count);
Packit Service 8a8a03
        dd_save_text(dd, FILENAME_COUNT, new_count_str);
Packit Service 8a8a03
Packit Service 8a8a03
        /* This condition can be simplified to either
Packit Service 8a8a03
         * (status * != 0 && * dup_of_dir) or (count == 1). But the
Packit Service 8a8a03
         * chosen form is much more reliable and safe. We must not call
Packit Service 8a8a03
         * dd_opendir() to locked dd otherwise we go into a deadlock.
Packit Service 8a8a03
         */
Packit Service 8a8a03
        if (strcmp(dd->dd_dirname, dirname) != 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            /* Update the last occurrence file by the time file of the new problem */
Packit Service 8a8a03
            struct dump_dir *new_dd = dd_opendir(dirname, DD_OPEN_READONLY);
Packit Service 8a8a03
            char *last_ocr = NULL;
Packit Service 8a8a03
            if (new_dd)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                /* TIME must exists in a valid dump directory but we don't want to die
Packit Service 8a8a03
                 * due to broken duplicated dump directory */
Packit Service 8a8a03
                last_ocr = dd_load_text_ext(new_dd, FILENAME_TIME,
Packit Service 8a8a03
                            DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT);
Packit Service 8a8a03
                dd_close(new_dd);
Packit Service 8a8a03
            }
Packit Service 8a8a03
            else
Packit Service 8a8a03
            {   /* dd_opendir() already produced a message with good information about failure */
Packit Service 8a8a03
                error_msg("Can't read the last occurrence file from the new dump directory.");
Packit Service 8a8a03
            }
Packit Service 8a8a03
Packit Service 8a8a03
            if (!last_ocr)
Packit Service 8a8a03
            {   /* the new dump directory may lie in the dump location for some time */
Packit Service 8a8a03
                log_warning("Using current time for the last occurrence file which may be incorrect.");
Packit Service 8a8a03
                time_t t = time(NULL);
Packit Service 8a8a03
                last_ocr = xasprintf("%lu", (long)t);
Packit Service 8a8a03
            }
Packit Service 8a8a03
Packit Service 8a8a03
            dd_save_text(dd, FILENAME_LAST_OCCURRENCE, last_ocr);
Packit Service 8a8a03
Packit Service 8a8a03
            free(last_ocr);
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* Reset mode/uig/gid to correct values for all files created by event run */
Packit Service 8a8a03
    dd_sanitize_mode_and_owner(dd);
Packit Service 8a8a03
Packit Service 8a8a03
    dd_close(dd);
Packit Service 8a8a03
Packit Service 8a8a03
    if (!dup_of_dir)
Packit Service 8a8a03
        log_notice("New problem directory %s, processing", work_dir);
Packit Service 8a8a03
    else
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_warning("Deleting problem directory %s (dup of %s)",
Packit Service 8a8a03
                    strrchr(dirname, '/') + 1,
Packit Service 8a8a03
                    strrchr(dup_of_dir, '/') + 1);
Packit Service 8a8a03
        delete_dump_dir(dirname);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* Run "notify[-dup]" event */
Packit Service 8a8a03
    int fd;
Packit Service 8a8a03
    child_pid = spawn_event_handler_child(
Packit Service 8a8a03
                work_dir,
Packit Service 8a8a03
                (dup_of_dir ? "notify-dup" : "notify"),
Packit Service 8a8a03
                &fd
Packit Service 8a8a03
    );
Packit Service 8a8a03
    //log_warning("Started notify, fd %d -> %d", fd, child_stdout_fd);
Packit Service 8a8a03
    xmove_fd(fd, child_stdout_fd);
Packit Service 8a8a03
    child_is_post_create = 0;
Packit Service 8a8a03
    if (dup_of_dir)
Packit Service 8a8a03
        RESPONSE_SETTER(resp, 303, dup_of_dir);
Packit Service 8a8a03
    else
Packit Service 8a8a03
    {
Packit Service 8a8a03
        RESPONSE_SETTER(resp, 200, NULL);
Packit Service 8a8a03
        free(dup_of_dir);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    dup_of_dir = NULL;
Packit Service 8a8a03
    strbuf_clear(cmd_output);
Packit Service 8a8a03
    goto read_child_output;
Packit Service 8a8a03
Packit Service 8a8a03
 delete_bad_dir:
Packit Service 8a8a03
    log_warning("Deleting problem directory '%s'", dirname);
Packit Service 8a8a03
    delete_dump_dir(dirname);
Packit Service 8a8a03
    /* TODO - better code to allow detection on client's side */
Packit Service 8a8a03
    RESPONSE_SETTER(resp, 403, NULL);
Packit Service 8a8a03
Packit Service 8a8a03
 ret:
Packit Service 8a8a03
    strbuf_free(cmd_output);
Packit Service 8a8a03
    free(dup_of_dir);
Packit Service 8a8a03
    close(child_stdout_fd);
Packit Service 8a8a03
    return 0;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
/* Create a new problem directory from client session.
Packit Service 8a8a03
 * Caller must ensure that all fields in struct client
Packit Service 8a8a03
 * are properly filled.
Packit Service 8a8a03
 */
Packit Service 8a8a03
static int create_problem_dir(GHashTable *problem_info, unsigned pid)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* Exit if free space is less than 1/4 of MaxCrashReportsSize */
Packit Service 8a8a03
    if (g_settings_nMaxCrashReportsSize > 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (low_free_space(g_settings_nMaxCrashReportsSize, g_settings_dump_location))
Packit Service 8a8a03
            exit(1);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* Create temp directory with the problem data.
Packit Service 8a8a03
     * This directory is renamed to final directory name after
Packit Service 8a8a03
     * all files have been stored into it.
Packit Service 8a8a03
     */
Packit Service 8a8a03
Packit Service 8a8a03
    gchar *dir_basename = g_hash_table_lookup(problem_info, "basename");
Packit Service 8a8a03
    if (!dir_basename)
Packit Service 8a8a03
        dir_basename = g_hash_table_lookup(problem_info, FILENAME_TYPE);
Packit Service 8a8a03
Packit Service 8a8a03
    char *path = xasprintf("%s/%s-%s-%u.new",
Packit Service 8a8a03
                           g_settings_dump_location,
Packit Service 8a8a03
                           dir_basename,
Packit Service 8a8a03
                           iso_date_string(NULL),
Packit Service 8a8a03
                           pid);
Packit Service 8a8a03
Packit Service 8a8a03
    /* This item is useless, don't save it */
Packit Service 8a8a03
    g_hash_table_remove(problem_info, "basename");
Packit Service 8a8a03
Packit Service 8a8a03
    /* No need to check the path length, as all variables used are limited,
Packit Service 8a8a03
     * and dd_create() fails if the path is too long.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    struct dump_dir *dd = dd_create(path, /*fs owner*/0, DEFAULT_DUMP_DIR_MODE);
Packit Service 8a8a03
    if (!dd)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        error_msg_and_die("Error creating problem directory '%s'", path);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    const int proc_dir_fd = open_proc_pid_dir(pid);
Packit Service 8a8a03
    char *rootdir = NULL;
Packit Service 8a8a03
Packit Service 8a8a03
    if (proc_dir_fd < 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        pwarn_msg("Cannot open /proc/%d:", pid);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else if (process_has_own_root_at(proc_dir_fd))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        /* Obtain the root directory path only if process' root directory is
Packit Service 8a8a03
         * not the same as the init's root directory
Packit Service 8a8a03
         */
Packit Service 8a8a03
        rootdir = get_rootdir_at(proc_dir_fd);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* Reading data from an arbitrary root directory is not secure. */
Packit Service 8a8a03
    if (proc_dir_fd >= 0 && g_settings_explorechroots)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        char proc_pid_root[sizeof("/proc/[pid]/root") + sizeof(pid_t) * 3];
Packit Service 8a8a03
        const size_t w = snprintf(proc_pid_root, sizeof(proc_pid_root), "/proc/%d/root", pid);
Packit Service 8a8a03
        assert(sizeof(proc_pid_root) > w);
Packit Service 8a8a03
Packit Service 8a8a03
        /* Yes, test 'rootdir' but use 'source_filename' because 'rootdir' can
Packit Service 8a8a03
         * be '/' for a process with own namespace. 'source_filename' is /proc/[pid]/root. */
Packit Service 8a8a03
        dd_create_basic_files(dd, client_uid, (rootdir != NULL) ? proc_pid_root : NULL);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else
Packit Service 8a8a03
    {
Packit Service 8a8a03
        dd_create_basic_files(dd, client_uid, NULL);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    if (proc_dir_fd >= 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        /* Obtain and save the command line. */
Packit Service 8a8a03
        char *cmdline = get_cmdline_at(proc_dir_fd);
Packit Service 8a8a03
        if (cmdline)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            dd_save_text(dd, FILENAME_CMDLINE, cmdline);
Packit Service 8a8a03
            free(cmdline);
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        /* Obtain and save the environment variables. */
Packit Service 8a8a03
        char *environ = get_environ_at(proc_dir_fd);
Packit Service 8a8a03
        if (environ)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            dd_save_text(dd, FILENAME_ENVIRON, environ);
Packit Service 8a8a03
            free(environ);
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        dd_copy_file_at(dd, FILENAME_CGROUP,    proc_dir_fd, "cgroup");
Packit Service 8a8a03
        dd_copy_file_at(dd, FILENAME_MOUNTINFO, proc_dir_fd, "mountinfo");
Packit Service 8a8a03
Packit Service 8a8a03
        FILE *open_fds = dd_open_item_file(dd, FILENAME_OPEN_FDS, O_RDWR);
Packit Service 8a8a03
        if (open_fds)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (dump_fd_info_at(proc_dir_fd, open_fds) < 0)
Packit Service 8a8a03
                dd_delete_item(dd, FILENAME_OPEN_FDS);
Packit Service 8a8a03
            fclose(open_fds);
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        const int init_proc_dir_fd = open_proc_pid_dir(1);
Packit Service 8a8a03
        FILE *namespaces = dd_open_item_file(dd, FILENAME_NAMESPACES, O_RDWR);
Packit Service 8a8a03
        if (namespaces && init_proc_dir_fd >= 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (dump_namespace_diff_at(init_proc_dir_fd, proc_dir_fd, namespaces) < 0)
Packit Service 8a8a03
                dd_delete_item(dd, FILENAME_NAMESPACES);
Packit Service 8a8a03
        }
Packit Service 8a8a03
        if (init_proc_dir_fd >= 0)
Packit Service 8a8a03
            close(init_proc_dir_fd);
Packit Service 8a8a03
        if (namespaces)
Packit Service 8a8a03
            fclose(namespaces);
Packit Service 8a8a03
Packit Service 8a8a03
        /* The process's root directory isn't the same as the init's root
Packit Service 8a8a03
         * directory. */
Packit Service 8a8a03
        if (rootdir)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (strcmp(rootdir, "/") ==  0)
Packit Service 8a8a03
            {   /* We are dealing containerized process because root's
Packit Service 8a8a03
                 * directory path is '/' and that means that the process
Packit Service 8a8a03
                 * has mounted its own root.
Packit Service 8a8a03
                 * Seriously, it is possible if the process is running in its
Packit Service 8a8a03
                 * own MOUNT namespaces.
Packit Service 8a8a03
                 */
Packit Service 8a8a03
                log_debug("Process %d is considered to be containerized", pid);
Packit Service 8a8a03
                pid_t container_pid;
Packit Service 8a8a03
                if (get_pid_of_container_at(proc_dir_fd, &container_pid) == 0)
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    char *container_cmdline = get_cmdline(container_pid);
Packit Service 8a8a03
                    dd_save_text(dd, FILENAME_CONTAINER_CMDLINE, container_cmdline);
Packit Service 8a8a03
                    free(container_cmdline);
Packit Service 8a8a03
                }
Packit Service 8a8a03
            }
Packit Service 8a8a03
            else
Packit Service 8a8a03
            {   /* We are dealing chrooted process. */
Packit Service 8a8a03
                dd_save_text(dd, FILENAME_ROOTDIR, rootdir);
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
        close(proc_dir_fd);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    free(rootdir);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Store id of the user whose application crashed. */
Packit Service 8a8a03
    char uid_str[sizeof(long) * 3 + 2];
Packit Service 8a8a03
    sprintf(uid_str, "%lu", (long)client_uid);
Packit Service 8a8a03
    dd_save_text(dd, FILENAME_UID, uid_str);
Packit Service 8a8a03
Packit Service 8a8a03
    GHashTableIter iter;
Packit Service 8a8a03
    gpointer gpkey;
Packit Service 8a8a03
    gpointer gpvalue;
Packit Service 8a8a03
    g_hash_table_iter_init(&iter, problem_info);
Packit Service 8a8a03
    while (g_hash_table_iter_next(&iter, &gpkey, &gpvalue))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        dd_save_text(dd, (gchar *) gpkey, (gchar *) gpvalue);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
Packit Service 8a8a03
Packit Service 8a8a03
    dd_close(dd);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Not needing it anymore */
Packit Service 8a8a03
    g_hash_table_destroy(problem_info);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Move the completely created problem directory
Packit Service 8a8a03
     * to final directory.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    char *newpath = xstrndup(path, strlen(path) - strlen(".new"));
Packit Service 8a8a03
    if (rename(path, newpath) == 0)
Packit Service 8a8a03
        strcpy(path, newpath);
Packit Service 8a8a03
    free(newpath);
Packit Service 8a8a03
Packit Service 8a8a03
    log_notice("Saved problem directory of pid %u to '%s'", pid, path);
Packit Service 8a8a03
Packit Service 8a8a03
    /* We let the peer know that problem dir was created successfully
Packit Service 8a8a03
     * _before_ we run potentially long-running post-create.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    printf("HTTP/1.1 201 Created\r\n\r\n");
Packit Service 8a8a03
    fflush(NULL);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Closing STDIN_FILENO (abrtd duped the socket to stdin and stdout) and
Packit Service 8a8a03
     * not-replacing it with something else to let abrt-server die on reading
Packit Service 8a8a03
     * from invalid stdin - to catch bugs. */
Packit Service 8a8a03
    close(STDIN_FILENO);
Packit Service 8a8a03
    close(STDOUT_FILENO);
Packit Service 8a8a03
    xdup2(STDERR_FILENO, STDOUT_FILENO); /* paranoia: don't leave stdout fd closed */
Packit Service 8a8a03
Packit Service 8a8a03
    /* Trim old problem directories if necessary */
Packit Service 8a8a03
    if (g_settings_nMaxCrashReportsSize > 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        trim_problem_dirs(g_settings_dump_location, g_settings_nMaxCrashReportsSize * (double)(1024*1024), path);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    run_post_create(path, NULL);
Packit Service 8a8a03
Packit Service 8a8a03
    /* free(path); */
Packit Service 8a8a03
    exit(0);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static gboolean key_value_ok(gchar *key, gchar *value)
Packit Service 8a8a03
{
Packit Service 8a8a03
    char *i;
Packit Service 8a8a03
Packit Service 8a8a03
    /* check key, it has to be valid filename and will end up in the
Packit Service 8a8a03
     * bugzilla */
Packit Service 8a8a03
    for (i = key; *i != 0; i++)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (!isalpha(*i) && (*i != '-') && (*i != '_') && (*i != ' '))
Packit Service 8a8a03
            return FALSE;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* check value of 'basename', it has to be valid non-hidden directory
Packit Service 8a8a03
     * name */
Packit Service 8a8a03
    if (strcmp(key, "basename") == 0
Packit Service 8a8a03
     || strcmp(key, FILENAME_TYPE) == 0
Packit Service 8a8a03
    )
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (!str_is_correct_filename(value))
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("Value of '%s' ('%s') is not a valid directory name",
Packit Service 8a8a03
                      key, value);
Packit Service 8a8a03
            return FALSE;
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return allowed_new_user_problem_entry(client_uid, key, value);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
/* Handles a message received from client over socket. */
Packit Service 8a8a03
static void process_message(GHashTable *problem_info, char *message)
Packit Service 8a8a03
{
Packit Service 8a8a03
    gchar *key, *value;
Packit Service 8a8a03
Packit Service 8a8a03
    value = strchr(message, '=');
Packit Service 8a8a03
    if (value)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        key = g_ascii_strdown(message, value - message); /* result is malloced */
Packit Service 8a8a03
//TODO: is it ok? it uses g_malloc, not malloc!
Packit Service 8a8a03
Packit Service 8a8a03
        value++;
Packit Service 8a8a03
        if (key_value_ok(key, value))
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (strcmp(key, FILENAME_UID) == 0)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                error_msg("Ignoring value of %s, will be determined later",
Packit Service 8a8a03
                          FILENAME_UID);
Packit Service 8a8a03
            }
Packit Service 8a8a03
            else
Packit Service 8a8a03
            {
Packit Service 8a8a03
                g_hash_table_insert(problem_info, key, xstrdup(value));
Packit Service 8a8a03
                /* Prevent freeing key later: */
Packit Service 8a8a03
                key = NULL;
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else
Packit Service 8a8a03
        {
Packit Service 8a8a03
            /* should use error_msg_and_die() here? */
Packit Service 8a8a03
            error_msg("Invalid key or value format: %s", message);
Packit Service 8a8a03
        }
Packit Service 8a8a03
        free(key);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else
Packit Service 8a8a03
    {
Packit Service 8a8a03
        /* should use error_msg_and_die() here? */
Packit Service 8a8a03
        error_msg("Invalid message format: '%s'", message);
Packit Service 8a8a03
    }
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void die_if_data_is_missing(GHashTable *problem_info)
Packit Service 8a8a03
{
Packit Service 8a8a03
    gboolean missing_data = FALSE;
Packit Service 8a8a03
    gchar **pstring;
Packit Service 8a8a03
    static const gchar *const needed[] = {
Packit Service 8a8a03
        FILENAME_TYPE,
Packit Service 8a8a03
        FILENAME_REASON,
Packit Service 8a8a03
        /* FILENAME_BACKTRACE, - ECC errors have no such elements */
Packit Service 8a8a03
        /* FILENAME_EXECUTABLE, */
Packit Service 8a8a03
        NULL
Packit Service 8a8a03
    };
Packit Service 8a8a03
Packit Service 8a8a03
    for (pstring = (gchar**) needed; *pstring; pstring++)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (!g_hash_table_lookup(problem_info, *pstring))
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("Element '%s' is missing", *pstring);
Packit Service 8a8a03
            missing_data = TRUE;
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    if (missing_data)
Packit Service 8a8a03
        error_msg_and_die("Some data is missing, aborting");
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
/*
Packit Service 8a8a03
 * Takes hash table, looks for key FILENAME_PID and tries to convert its value
Packit Service 8a8a03
 * to int.
Packit Service 8a8a03
 */
Packit Service 8a8a03
unsigned convert_pid(GHashTable *problem_info)
Packit Service 8a8a03
{
Packit Service 8a8a03
    long ret;
Packit Service 8a8a03
    gchar *pid_str = (gchar *) g_hash_table_lookup(problem_info, FILENAME_PID);
Packit Service 8a8a03
    char *err_pos;
Packit Service 8a8a03
Packit Service 8a8a03
    if (!pid_str)
Packit Service 8a8a03
        error_msg_and_die("PID data is missing, aborting");
Packit Service 8a8a03
Packit Service 8a8a03
    errno = 0;
Packit Service 8a8a03
    ret = strtol(pid_str, &err_pos, 10);
Packit Service 8a8a03
    if (errno || pid_str == err_pos || *err_pos != '\0'
Packit Service 8a8a03
        || ret > UINT_MAX || ret < 1)
Packit Service 8a8a03
        error_msg_and_die("Malformed or out-of-range PID number: '%s'", pid_str);
Packit Service 8a8a03
Packit Service 8a8a03
    return (unsigned) ret;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static int perform_http_xact(struct response *rsp)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* use free instead of g_free so that we can use xstr* functions from
Packit Service 8a8a03
     * libreport/lib/xfuncs.c
Packit Service 8a8a03
     */
Packit Service 8a8a03
    GHashTable *problem_info = g_hash_table_new_full(g_str_hash, g_str_equal,
Packit Service 8a8a03
                                     free, free);
Packit Service 8a8a03
    /* Read header */
Packit Service 8a8a03
    char *body_start = NULL;
Packit Service 8a8a03
    char *messagebuf_data = NULL;
Packit Service 8a8a03
    unsigned messagebuf_len = 0;
Packit Service 8a8a03
    /* Loop until EOF/error/timeout/end_of_header */
Packit Service 8a8a03
    while (1)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
Packit Service 8a8a03
        char *p = messagebuf_data + messagebuf_len;
Packit Service 8a8a03
        int rd = read(STDIN_FILENO, p, INPUT_BUFFER_SIZE);
Packit Service 8a8a03
        if (rd < 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (errno == EINTR) /* SIGALRM? */
Packit Service 8a8a03
                error_msg_and_die("Timed out");
Packit Service 8a8a03
            perror_msg_and_die("read");
Packit Service 8a8a03
        }
Packit Service 8a8a03
        if (rd == 0)
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        log_debug("Received %u bytes of data", rd);
Packit Service 8a8a03
        messagebuf_len += rd;
Packit Service 8a8a03
        total_bytes_read += rd;
Packit Service 8a8a03
        if (total_bytes_read > MAX_MESSAGE_SIZE)
Packit Service 8a8a03
            error_msg_and_die("Message is too long, aborting");
Packit Service 8a8a03
Packit Service 8a8a03
        /* Check whether we see end of header */
Packit Service 8a8a03
        /* Note: we support both [\r]\n\r\n and \n\n */
Packit Service 8a8a03
        char *past_end = messagebuf_data + messagebuf_len;
Packit Service 8a8a03
        if (p > messagebuf_data+1)
Packit Service 8a8a03
            p -= 2; /* start search from two last bytes in last read - they might be '\n\r' */
Packit Service 8a8a03
        while (p < past_end)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            p = memchr(p, '\n', past_end - p);
Packit Service 8a8a03
            if (!p)
Packit Service 8a8a03
                break;
Packit Service 8a8a03
            p++;
Packit Service 8a8a03
            if (p >= past_end)
Packit Service 8a8a03
                break;
Packit Service 8a8a03
            if (*p == '\n'
Packit Service 8a8a03
             || (*p == '\r' && p+1 < past_end && p[1] == '\n')
Packit Service 8a8a03
            ) {
Packit Service 8a8a03
                body_start = p + 1 + (*p == '\r');
Packit Service 8a8a03
                *p = '\0';
Packit Service 8a8a03
                goto found_end_of_header;
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
    } /* while (read) */
Packit Service 8a8a03
 found_end_of_header: ;
Packit Service 8a8a03
    log_debug("Request: %s", messagebuf_data);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Sanitize and analyze header.
Packit Service 8a8a03
     * Header now is in messagebuf_data, NUL terminated string,
Packit Service 8a8a03
     * with last empty line deleted (by placement of NUL).
Packit Service 8a8a03
     * \r\n are not (yet) converted to \n, multi-line headers also
Packit Service 8a8a03
     * not converted.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    /* First line must be "op<space>[http://host]/path<space>HTTP/n.n".
Packit Service 8a8a03
     * <space> is exactly one space char.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    if (prefixcmp(messagebuf_data, "DELETE ") == 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        messagebuf_data += strlen("DELETE ");
Packit Service 8a8a03
        char *space = strchr(messagebuf_data, ' ');
Packit Service 8a8a03
        if (!space || prefixcmp(space+1, "HTTP/") != 0)
Packit Service 8a8a03
            return 400; /* Bad Request */
Packit Service 8a8a03
        *space = '\0';
Packit Service 8a8a03
        //decode_url(messagebuf_data); %20 => ' '
Packit Service 8a8a03
        alarm(0);
Packit Service 8a8a03
        return delete_path(messagebuf_data);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* We erroneously used "PUT /" to create new problems.
Packit Service 8a8a03
     * POST is the correct request in this case:
Packit Service 8a8a03
     * "PUT /" implies creation or replace of resource named "/"!
Packit Service 8a8a03
     * Delete PUT in 2014.
Packit Service 8a8a03
     */
Packit Service 8a8a03
    if (prefixcmp(messagebuf_data, "PUT ") != 0
Packit Service 8a8a03
     && prefixcmp(messagebuf_data, "POST ") != 0
Packit Service 8a8a03
    ) {
Packit Service 8a8a03
        return 400; /* Bad Request */
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    enum {
Packit Service 8a8a03
        CREATION_NOTIFICATION,
Packit Service 8a8a03
        CREATION_REQUEST,
Packit Service 8a8a03
    };
Packit Service 8a8a03
    int url_type;
Packit Service 8a8a03
    char *url = skip_non_whitespace(messagebuf_data) + 1; /* skip "POST " */
Packit Service 8a8a03
    if (prefixcmp(url, "/creation_notification ") == 0)
Packit Service 8a8a03
        url_type = CREATION_NOTIFICATION;
Packit Service 8a8a03
    else if (prefixcmp(url, "/ ") == 0)
Packit Service 8a8a03
        url_type = CREATION_REQUEST;
Packit Service 8a8a03
    else
Packit Service 8a8a03
        return 400; /* Bad Request */
Packit Service 8a8a03
Packit Service 8a8a03
    /* Read body */
Packit Service 8a8a03
    if (!body_start)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_warning("Premature EOF detected, exiting");
Packit Service 8a8a03
        return 400; /* Bad Request */
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    messagebuf_len -= (body_start - messagebuf_data);
Packit Service 8a8a03
    memmove(messagebuf_data, body_start, messagebuf_len);
Packit Service 8a8a03
    log_debug("Body so far: %u bytes, '%s'", messagebuf_len, messagebuf_data);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Loop until EOF/error/timeout */
Packit Service 8a8a03
    while (1)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (url_type == CREATION_REQUEST)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            while (1)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                unsigned len = strnlen(messagebuf_data, messagebuf_len);
Packit Service 8a8a03
                if (len >= messagebuf_len)
Packit Service 8a8a03
                    break;
Packit Service 8a8a03
                /* messagebuf has at least one NUL - process the line */
Packit Service 8a8a03
                process_message(problem_info, messagebuf_data);
Packit Service 8a8a03
                messagebuf_len -= (len + 1);
Packit Service 8a8a03
                memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE + 1);
Packit Service 8a8a03
        int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE);
Packit Service 8a8a03
        if (rd < 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (errno == EINTR) /* SIGALRM? */
Packit Service 8a8a03
                error_msg_and_die("Timed out");
Packit Service 8a8a03
            perror_msg_and_die("read");
Packit Service 8a8a03
        }
Packit Service 8a8a03
        if (rd == 0)
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        log_debug("Received %u bytes of data", rd);
Packit Service 8a8a03
        messagebuf_len += rd;
Packit Service 8a8a03
        total_bytes_read += rd;
Packit Service 8a8a03
        if (total_bytes_read > MAX_MESSAGE_SIZE)
Packit Service 8a8a03
            error_msg_and_die("Message is too long, aborting");
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* Body received, EOF was seen. Don't let alarm to interrupt after this. */
Packit Service 8a8a03
    alarm(0);
Packit Service 8a8a03
Packit Service 8a8a03
    int ret = 0;
Packit Service 8a8a03
    if (url_type == CREATION_NOTIFICATION)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (client_uid != 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("UID=%ld is not authorized to trigger post-create processing", (long)client_uid);
Packit Service 8a8a03
            ret = 403; /* Forbidden */
Packit Service 8a8a03
            goto out;
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        messagebuf_data[messagebuf_len] = '\0';
Packit Service 8a8a03
        return run_post_create(messagebuf_data, rsp);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    die_if_data_is_missing(problem_info);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Save problem dir */
Packit Service 8a8a03
    char *executable = g_hash_table_lookup(problem_info, FILENAME_EXECUTABLE);
Packit Service 8a8a03
    if (executable)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        char *last_file = concat_path_file(g_settings_dump_location, "last-via-server");
Packit Service 8a8a03
        int repeating_crash = check_recent_crash_file(last_file, executable);
Packit Service 8a8a03
        free(last_file);
Packit Service 8a8a03
        if (repeating_crash) /* Only pretend that we saved it */
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("Not saving repeating crash in '%s'", executable);
Packit Service 8a8a03
            goto out; /* ret is 0: "success" */
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
#if 0
Packit Service 8a8a03
//TODO:
Packit Service 8a8a03
    /* At least it should generate local problem identifier UUID */
Packit Service 8a8a03
    problem_data_add_basics(problem_info);
Packit Service 8a8a03
//...the problem being that problem_info here is not a problem_data_t!
Packit Service 8a8a03
#endif
Packit Service 8a8a03
    unsigned pid = convert_pid(problem_info);
Packit Service 8a8a03
    struct ns_ids client_ids;
Packit Service 8a8a03
    if (get_ns_ids(client_pid, &client_ids) < 0)
Packit Service 8a8a03
        error_msg_and_die("Cannot get peer's Namespaces from /proc/%d/ns", client_pid);
Packit Service 8a8a03
Packit Service 8a8a03
    if (client_ids.nsi_ids[PROC_NS_ID_PID] != g_ns_ids.nsi_ids[PROC_NS_ID_PID])
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_notice("Client is running in own PID Namespace, using PID %d instead of %d", client_pid, pid);
Packit Service 8a8a03
        pid = client_pid;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    create_problem_dir(problem_info, pid);
Packit Service 8a8a03
    /* does not return */
Packit Service 8a8a03
Packit Service 8a8a03
 out:
Packit Service 8a8a03
    g_hash_table_destroy(problem_info);
Packit Service 8a8a03
    return ret; /* Used as HTTP response code */
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void dummy_handler(int sig_unused) {}
Packit Service 8a8a03
Packit Service 8a8a03
int main(int argc, char **argv)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* I18n */
Packit Service 8a8a03
    setlocale(LC_ALL, "");
Packit Service 8a8a03
#if ENABLE_NLS
Packit Service 8a8a03
    bindtextdomain(PACKAGE, LOCALEDIR);
Packit Service 8a8a03
    textdomain(PACKAGE);
Packit Service 8a8a03
#endif
Packit Service 8a8a03
Packit Service 8a8a03
    abrt_init(argv);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Can't keep these strings/structs static: _() doesn't support that */
Packit Service 8a8a03
    const char *program_usage_string = _(
Packit Service 8a8a03
        "& [options]"
Packit Service 8a8a03
    );
Packit Service 8a8a03
    enum {
Packit Service 8a8a03
        OPT_v = 1 << 0,
Packit Service 8a8a03
        OPT_u = 1 << 1,
Packit Service 8a8a03
        OPT_s = 1 << 2,
Packit Service 8a8a03
        OPT_p = 1 << 3,
Packit Service 8a8a03
    };
Packit Service 8a8a03
    /* Keep enum above and order of options below in sync! */
Packit Service 8a8a03
    struct options program_options[] = {
Packit Service 8a8a03
        OPT__VERBOSE(&g_verbose),
Packit Service 8a8a03
        OPT_INTEGER('u', NULL, &client_uid, _("Use NUM as client uid")),
Packit Service 8a8a03
        OPT_BOOL(   's', NULL, NULL       , _("Log to syslog")),
Packit Service 8a8a03
        OPT_BOOL(   'p', NULL, NULL       , _("Add program names to log")),
Packit Service 8a8a03
        OPT_END()
Packit Service 8a8a03
    };
Packit Service 8a8a03
    unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
Packit Service 8a8a03
Packit Service 8a8a03
    export_abrt_envvars(opts & OPT_p);
Packit Service 8a8a03
Packit Service 8a8a03
    msg_prefix = xasprintf("%s[%u]", g_progname, getpid());
Packit Service 8a8a03
    if (opts & OPT_s)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        logmode = LOGMODE_JOURNAL;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    /* Set up timeout handling */
Packit Service 8a8a03
    /* Part 1 - need this to make SIGALRM interrupt syscalls
Packit Service 8a8a03
     * (as opposed to restarting them): I want read syscall to be interrupted
Packit Service 8a8a03
     */
Packit Service 8a8a03
    struct sigaction sa;
Packit Service 8a8a03
    /* sa.sa_flags.SA_RESTART bit is clear: make signal interrupt syscalls */
Packit Service 8a8a03
    memset(&sa, 0, sizeof(sa));
Packit Service 8a8a03
    sa.sa_handler = dummy_handler; /* pity, SIG_DFL won't do */
Packit Service 8a8a03
    sigaction(SIGALRM, &sa, NULL);
Packit Service 8a8a03
    /* Part 2 - set the timeout per se */
Packit Service 8a8a03
    alarm(TIMEOUT);
Packit Service 8a8a03
Packit Service 8a8a03
    /* Get uid of the connected client */
Packit Service 8a8a03
    struct ucred cr;
Packit Service 8a8a03
    socklen_t crlen = sizeof(cr);
Packit Service 8a8a03
    if (0 != getsockopt(STDIN_FILENO, SOL_SOCKET, SO_PEERCRED, &cr, &crlen))
Packit Service 8a8a03
        perror_msg_and_die("getsockopt(SO_PEERCRED)");
Packit Service 8a8a03
    if (crlen != sizeof(cr))
Packit Service 8a8a03
        error_msg_and_die("%s: bad crlen %d", "getsockopt(SO_PEERCRED)", (int)crlen);
Packit Service 8a8a03
Packit Service 8a8a03
    if (client_uid == (uid_t)-1L)
Packit Service 8a8a03
        client_uid = cr.uid;
Packit Service 8a8a03
Packit Service 8a8a03
    client_pid = cr.pid;
Packit Service 8a8a03
Packit Service 8a8a03
    pid_t pid = getpid();
Packit Service 8a8a03
    if (get_ns_ids(getpid(), &g_ns_ids) < 0)
Packit Service 8a8a03
        error_msg_and_die("Cannot get own Namespaces from /proc/%d/ns", pid);
Packit Service 8a8a03
Packit Service 8a8a03
    load_abrt_conf();
Packit Service 8a8a03
Packit Service 8a8a03
    struct response rsp = { 0 };
Packit Service 8a8a03
    int r = perform_http_xact(&rsp;;
Packit Service 8a8a03
    if (r == 0)
Packit Service 8a8a03
        r = 200;
Packit Service 8a8a03
Packit Service 8a8a03
    if (rsp.code == 0)
Packit Service 8a8a03
        rsp.code = r;
Packit Service 8a8a03
Packit Service 8a8a03
    free_abrt_conf_data();
Packit Service 8a8a03
Packit Service 8a8a03
    printf("HTTP/1.1 %u \r\n\r\n", rsp.code);
Packit Service 8a8a03
    if (rsp.message != NULL)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        printf("%s", rsp.message);
Packit Service 8a8a03
        fflush(stdout);
Packit Service 8a8a03
        free(rsp.message);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return (r >= 400); /* Error if 400+ */
Packit Service 8a8a03
}