Blame src/daemon/abrt-server.c

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