Exemplo n.º 1
0
int report_problem_in_memory(problem_data_t *pd, int flags)
{
    int result = 0;
    struct dump_dir *dd = create_dump_dir_from_problem_data(pd, "/tmp"/* /var/tmp ?? */);
    if (!dd)
        return -1;
    char *dir_name = xstrdup(dd->dd_dirname);
    dd_close(dd);
    VERB2 log("Temp problem dir: '%s'", dir_name);

    if (!(flags & LIBREPORT_WAIT))
        flags |= LIBREPORT_DEL_DIR;
    result = report_problem_in_dir(dir_name, flags);

    /* If we waited for reporter to finish, we should clean up the tmp dir
     * (if we didn't, cleaning up will be done by reporting child process later).
     * We can also reload the problem data if requested.
     */
    if (flags & LIBREPORT_WAIT)
    {
        if (flags & LIBREPORT_RELOAD_DATA)
            g_hash_table_remove_all(pd);
        dd = dd_opendir(dir_name, 0);
        if (dd)
        {
            if (flags & LIBREPORT_RELOAD_DATA)
                problem_data_load_from_dump_dir(pd, dd, NULL);
            dd_delete(dd);
        }
    }

    free(dir_name);
    return result;
}
Exemplo n.º 2
0
/* Remove dump dir */
static int delete_path(const char *dump_dir_name)
{
    /* If doesn't start with "g_settings_dump_location/"... */
    if (!dir_is_in_dump_location(dump_dir_name))
    {
        /* Then refuse to operate on it (someone is attacking us??) */
        error_msg("Bad problem directory name '%s', should start with: '%s'", dump_dir_name, g_settings_dump_location);
        return 400; /* Bad Request */
    }
    if (!dir_has_correct_permissions(dump_dir_name, DD_PERM_DAEMONS))
    {
        error_msg("Problem directory '%s' has wrong owner or group", dump_dir_name);
        return 400; /*  */
    }

    struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_FD_ONLY);
    if (dd == NULL)
    {
        perror_msg("Can't open problem directory '%s'", dump_dir_name);
        return 400;
    }
    if (!dd_accessible_by_uid(dd, client_uid))
    {
        dd_close(dd);
        if (errno == ENOTDIR)
        {
            error_msg("Path '%s' isn't problem directory", dump_dir_name);
            return 404; /* Not Found */
        }
        error_msg("Problem directory '%s' can't be accessed by user with uid %ld", dump_dir_name, (long)client_uid);
        return 403; /* Forbidden */
    }

    dd = dd_fdopendir(dd, /*flags:*/ 0);
    if (dd)
    {
        if (dd_delete(dd) != 0)
        {
            error_msg("Failed to delete problem directory '%s'", dump_dir_name);
            dd_close(dd);
            return 400;
        }
    }

    return 0; /* success */
}
Exemplo n.º 3
0
/* Remove dump dir */
int DeleteDebugDump(const char *dump_dir_name, long caller_uid)
{
    /* If doesn't start with "DEBUG_DUMPS_DIR/"... */
    if (strncmp(dump_dir_name, DEBUG_DUMPS_DIR"/", strlen(DEBUG_DUMPS_DIR"/")) != 0
    /* or contains "/." anywhere (-> might contain ".." component) */
     || strstr(dump_dir_name + strlen(DEBUG_DUMPS_DIR), "/.")
    ) {
        /* Then refuse to operate on it (someone is attacking us??) */
        error_msg("Bad dump directory name '%s', not deleting", dump_dir_name);
        return MW_ERROR;
    }

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return MW_NOENT_ERROR;

    if (caller_uid != 0) /* not called by root */
    {
        char caller_uid_str[sizeof(long) * 3 + 2];
        sprintf(caller_uid_str, "%ld", caller_uid);

        char *uid = dd_load_text_ext(dd, FILENAME_UID, DD_FAIL_QUIETLY_ENOENT | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
        /* we assume that the dump_dir can be handled by everyone if uid == NULL
         * e.g: kerneloops
         */
        if (uid != NULL)
        {
            bool uid_matches = (strcmp(uid, caller_uid_str) == 0);
            free(uid);
            if (!uid_matches)
            {
                dd_close(dd);
                error_msg("Dump directory '%s' can't be accessed by user with uid %ld", dump_dir_name, caller_uid);
                return 1;
            }
        }
    }

    dd_delete(dd);

    return 0; /* success */
}
Exemplo n.º 4
0
int run_event_on_problem_data(struct run_event_state *state, problem_data_t *data, const char *event)
{
    state->children_count = 0;

    struct dump_dir *dd = create_dump_dir_from_problem_data(data, NULL);
    if (!dd)
        return -1;
    char *dir_name = xstrdup(dd->dd_dirname);
    dd_close(dd);

    int r = run_event_on_dir_name(state, dir_name, event);

    g_hash_table_remove_all(data);
    dd = dd_opendir(dir_name, /*flags:*/ 0);
    free(dir_name);
    if (dd)
    {
        problem_data_load_from_dump_dir(data, dd, NULL);
        dd_delete(dd);
    }

    return r;
}
Exemplo n.º 5
0
void runUserTask(uint32_t numTicks){
	printf("[User] Doing busy work for %u ticks.\n", numTicks);
	doBusyWorkForTicks(numTicks);
	printf("[User] Task complete.\n");
	dd_delete(_task_get_id());
}
Exemplo n.º 6
0
static int run_post_create(const char *dirname)
{
    /* If doesn't start with "g_settings_dump_location/"... */
    if (!dir_is_in_dump_location(dirname))
    {
        /* Then refuse to operate on it (someone is attacking us??) */
        error_msg("Bad problem directory name '%s', should start with: '%s'", dirname, g_settings_dump_location);
        return 400; /* Bad Request */
    }
    if (!dir_has_correct_permissions(dirname, DD_PERM_EVENTS))
    {
        error_msg("Problem directory '%s' has wrong owner or group", dirname);
        return 400; /*  */
    }
    /* Check completness */
    {
        struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY);

        char *provoker = NULL;
        const bool event_dir = dd && problem_dump_dir_was_provoked_by_abrt_event(dd, &provoker);
        if (event_dir)
        {
            if (g_settings_debug_level == 0)
            {
                error_msg("Removing problem provoked by ABRT(pid:%s): '%s'", provoker, dirname);
                dd_delete(dd);
            }
            else
            {
                char *dumpdir = NULL;
                char *event   = NULL;
                char *reason  = NULL;
                char *cmdline = NULL;

                /* Ignore errors */
                dd_get_env_variable(dd, "DUMP_DIR", &dumpdir);
                dd_get_env_variable(dd, "EVENT",    &event);
                reason  = dd_load_text(dd, FILENAME_REASON);
                cmdline = dd_load_text(dd, FILENAME_CMDLINE);

                error_msg("ABRT_SERVER_PID=%s;DUMP_DIR='%s';EVENT='%s';REASON='%s';CMDLINE='%s'",
                           provoker, dumpdir, event, reason, cmdline);

            }

            free(provoker);
            return 400;
        }

        const bool complete = dd && problem_dump_dir_is_complete(dd);
        dd_close(dd);
        if (complete)
        {
            error_msg("Problem directory '%s' has already been processed", dirname);
            return 403;
        }
    }

    int child_stdout_fd;
    int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd);

    char *dup_of_dir = NULL;
    struct strbuf *cmd_output = strbuf_new();

    bool child_is_post_create = 1; /* else it is a notify child */

 read_child_output:
    //log("Reading from event fd %d", child_stdout_fd);

    /* Read streamed data and split lines */
    for (;;)
    {
        char buf[250]; /* usually we get one line, no need to have big buf */
        errno = 0;
        int r = safe_read(child_stdout_fd, buf, sizeof(buf) - 1);
        if (r <= 0)
            break;
        buf[r] = '\0';

        /* split lines in the current buffer */
        char *raw = buf;
        char *newline;
        while ((newline = strchr(raw, '\n')) != NULL)
        {
            *newline = '\0';
            strbuf_append_str(cmd_output, raw);
            char *msg = cmd_output->buf;

            if (child_is_post_create
             && prefixcmp(msg, "DUP_OF_DIR: ") == 0
            ) {
                free(dup_of_dir);
                dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: "));
            }
            else
                log("%s", msg);

            strbuf_clear(cmd_output);
            /* jump to next line */
            raw = newline + 1;
        }

        /* beginning of next line. the line continues by next read */
        strbuf_append_str(cmd_output, raw);
    }

    /* EOF/error */

    /* Wait for child to actually exit, collect status */
    int status = 0;
    if (safe_waitpid(child_pid, &status, 0) <= 0)
    /* should not happen */
        perror_msg("waitpid(%d)", child_pid);

    /* If it was a "notify[-dup]" event, then we're done */
    if (!child_is_post_create)
        goto ret;

    /* exit 0 means "this is a good, non-dup dir" */
    /* exit with 1 + "DUP_OF_DIR: dir" string => dup */
    if (status != 0)
    {
        if (WIFSIGNALED(status))
        {
            log("'post-create' on '%s' killed by signal %d",
                            dirname, WTERMSIG(status));
            goto delete_bad_dir;
        }
        /* else: it is WIFEXITED(status) */
        if (!dup_of_dir)
        {
            log("'post-create' on '%s' exited with %d",
                            dirname, WEXITSTATUS(status));
            goto delete_bad_dir;
        }
    }

    const char *work_dir = (dup_of_dir ? dup_of_dir : dirname);

    /* Load problem_data (from the *first dir* if this one is a dup) */
    struct dump_dir *dd = dd_opendir(work_dir, /*flags:*/ 0);
    if (!dd)
        /* dd_opendir already emitted error msg */
        goto delete_bad_dir;

    /* Update count */
    char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT);
    unsigned long count = strtoul(count_str, NULL, 10);

    /* Don't increase crash count if we are working with newly uploaded
     * directory (remote crash) which already has its crash count set.
     */
    if ((status != 0 && dup_of_dir) || count == 0)
    {
        count++;
        char new_count_str[sizeof(long)*3 + 2];
        sprintf(new_count_str, "%lu", count);
        dd_save_text(dd, FILENAME_COUNT, new_count_str);

        /* This condition can be simplified to either
         * (status * != 0 && * dup_of_dir) or (count == 1). But the
         * chosen form is much more reliable and safe. We must not call
         * dd_opendir() to locked dd otherwise we go into a deadlock.
         */
        if (strcmp(dd->dd_dirname, dirname) != 0)
        {
            /* Update the last occurrence file by the time file of the new problem */
            struct dump_dir *new_dd = dd_opendir(dirname, DD_OPEN_READONLY);
            char *last_ocr = NULL;
            if (new_dd)
            {
                /* TIME must exists in a valid dump directory but we don't want to die
                 * due to broken duplicated dump directory */
                last_ocr = dd_load_text_ext(new_dd, FILENAME_TIME,
                            DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT);
                dd_close(new_dd);
            }
            else
            {   /* dd_opendir() already produced a message with good information about failure */
                error_msg("Can't read the last occurrence file from the new dump directory.");
            }

            if (!last_ocr)
            {   /* the new dump directory may lie in the dump location for some time */
                log("Using current time for the last occurrence file which may be incorrect.");
                time_t t = time(NULL);
                last_ocr = xasprintf("%lu", (long)t);
            }

            dd_save_text(dd, FILENAME_LAST_OCCURRENCE, last_ocr);

            free(last_ocr);
        }
    }

    /* Reset mode/uig/gid to correct values for all files created by event run */
    dd_sanitize_mode_and_owner(dd);

    dd_close(dd);

    if (!dup_of_dir)
        log_notice("New problem directory %s, processing", work_dir);
    else
    {
        log_warning("Deleting problem directory %s (dup of %s)",
                    strrchr(dirname, '/') + 1,
                    strrchr(dup_of_dir, '/') + 1);
        delete_dump_dir(dirname);
    }

    /* Run "notify[-dup]" event */
    int fd;
    child_pid = spawn_event_handler_child(
                work_dir,
                (dup_of_dir ? "notify-dup" : "notify"),
                &fd
    );
    //log("Started notify, fd %d -> %d", fd, child_stdout_fd);
    xmove_fd(fd, child_stdout_fd);
    child_is_post_create = 0;
    strbuf_clear(cmd_output);
    free(dup_of_dir);
    dup_of_dir = NULL;
    goto read_child_output;

 delete_bad_dir:
    log_warning("Deleting problem directory '%s'", dirname);
    delete_dump_dir(dirname);

 ret:
    strbuf_free(cmd_output);
    free(dup_of_dir);
    close(child_stdout_fd);
    return 0;
}
Exemplo n.º 7
0
/* Queueing the process will also lead to cleaning up the dump location.
 */
static void queue_post_craete_process(struct abrt_server_proc *proc)
{
    load_abrt_conf();
    struct abrt_server_proc *running = s_dir_queue == NULL ? NULL
                                                           : (struct abrt_server_proc *)s_dir_queue->data;
    if (g_settings_nMaxCrashReportsSize == 0)
        goto consider_processing;

    const char *full_path_ignored = running != NULL ? running->dirname
                                                    : proc->dirname;
    const char *ignored = strrchr(full_path_ignored, '/');
    if (NULL == ignored)
        /* Paranoia, this should not happen. */
        ignored = full_path_ignored;
    else
        /* Move behind '/' */
        ++ignored;

    char *worst_dir = NULL;
    const double max_size = 1024 * 1024 * g_settings_nMaxCrashReportsSize;
    while (get_dirsize_find_largest_dir(g_settings_dump_location, &worst_dir, ignored) >= max_size
           && worst_dir)
    {
        const char *kind = "old";

        GList *proc_of_deleted_item = NULL;
        if (proc != NULL && strcmp(worst_dir, proc->dirname) == 0)
        {
            kind = "new";
            stop_abrt_server(proc);
            proc = NULL;
        }
        else if ((proc_of_deleted_item = g_list_find_custom(s_dir_queue, worst_dir, (GCompareFunc)abrt_server_compare_dirname)))
        {
            kind = "unprocessed";
            struct abrt_server_proc *removed_proc = (struct abrt_server_proc *)proc_of_deleted_item->data;
            s_dir_queue = g_list_delete_link(s_dir_queue, proc_of_deleted_item);
            stop_abrt_server(removed_proc);
        }

        log_warning("Size of '%s' >= %u MB (MaxCrashReportsSize), deleting %s directory '%s'",
                g_settings_dump_location, g_settings_nMaxCrashReportsSize,
                kind, worst_dir);

        char *deleted = concat_path_file(g_settings_dump_location, worst_dir);
        free(worst_dir);
        worst_dir = NULL;

        struct dump_dir *dd = dd_opendir(deleted, DD_FAIL_QUIETLY_ENOENT);
        if (dd != NULL)
            dd_delete(dd);

        free(deleted);
    }

consider_processing:
    /* If the process survived cleaning up the dump location, append it to the
     * post-create queue.
     */
    if (proc != NULL)
        s_dir_queue = g_list_append(s_dir_queue, proc);

    /* If there were no running post-crate process before we added the
     * currently handled process to the post-create queue, start processing of
     * the currently handled process.
     */
    if (running == NULL)
        notify_next_post_create_process(NULL/*finished*/);
}
Exemplo n.º 8
0
int report_problem_in_dir(const char *dirname, int flags)
{
    /* Prepare it before fork, to avoid thread-unsafe setenv there */
    char *prgname = (char*) g_get_prgname();
    if (prgname)
        prgname = xasprintf("LIBREPORT_PRGNAME=%s", prgname);

    fflush(NULL);

    pid_t pid = fork();
    if (pid < 0) /* error */
    {
        perror_msg("fork");
        return -1;
    }

    if (pid == 0) /* child */
    {
        const char *event_name;
        const char *path, *path1, *path2;
        char *args[7], **pp;

        /* Graphical tool */
        event_name = "report-gui";
        path1 = BIN_DIR"/report-gtk";
        path2 = "report-gtk";
        pp = args;
        *pp++ = (char *)"report-gtk";
        if (flags & LIBREPORT_DEL_DIR)
            *pp++ = (char *)"--delete";
        *pp++ = (char *)"--";
        *pp++ = (char *)dirname;
        *pp = NULL;

        if (prgname)
            putenv(prgname);

        if (flags & LIBREPORT_RUN_NEWT)
        {
            /* we want to run newt first */
            event_name = "report-cli";
            path1 = BIN_DIR"/report-newt";
            path2 = "report-newt";
            pp = args;
            *pp++ = (char *)"report-newt";
            if (flags & LIBREPORT_DEL_DIR)
                *pp++ = (char *)"--delete";
            *pp++ = (char *)"--";
            *pp++ = (char *)dirname;
            *pp = NULL;
        }
        else if (!getenv("DISPLAY") || (flags & LIBREPORT_RUN_CLI))
        {
            /* GUI won't work, use command line tool instead */
            event_name = "report-cli";
            path1 = BIN_DIR"/report-cli";
            path2 = "report-cli";
            pp = args;
            *pp++ = (char *)"report-cli";
            if (flags & LIBREPORT_DEL_DIR)
                *pp++ = (char *)"--delete";
            *pp++ = (char *)"-e";
            *pp++ = (char *)"report-cli";
            *pp++ = (char *)"--";
            *pp++ = (char *)dirname;
            *pp = NULL;
        }

        /* Some callers set SIGCHLD to SIG_IGN.
         * However, reporting spawns child processes.
         * Suppressing child death notification terribly confuses some of them.
         * Just in case, undo it.
         * Note that we do it in the child, so the parent is never affected.
         */
        signal(SIGCHLD, SIG_DFL);

        if (!(flags & (LIBREPORT_WAIT | LIBREPORT_GETPID)))
        {
            /* Caller doesn't want to wait for completion (!LIBREPORT_WAIT),
             * and doesn't want to have pid returned (!LIBREPORT_GETPID).
             * Create a grandchild, and then exit.
             * This reparents grandchild to init, and makes waitpid
             * in parent detect our exit and return almost immediately.
             */
            pid_t pid = fork();
            if (pid < 0) /* error */
                perror_msg_and_die("fork");
            if (pid != 0) /* not grandchild */
            {
                /* And now we exit: */
                _exit(0);
            }
            /* There's an alternative approach to achieve this,
             * instead of using --delete.
             * We can create yet another intermediate process which
             * waits for reporting child to finish, and then
             * removes temporary dump dir.
             * Pros: deletion becomes more robust.
             * Even if child crashes, dir will be deleted.
             * Cons: having another process would use some resources,
             * and we'll need to at least close all open file descriptors,
             * and reopen stdio to /dev/null. We also might keep
             * a lot of libraries loaded:
             * who knows what parent process links against.
             * (can be worked around by exec'ing a "wait & delete" helper)
             */
        }

        struct run_event_state *run_state = new_run_event_state();
        int r = run_event_on_dir_name(run_state, dirname, event_name);
        int no_such_event = (r == 0 && run_state->children_count == 0);
        free_run_event_state(run_state);
        if (!no_such_event)
        {
            if (flags & LIBREPORT_DEL_DIR)
            {
                struct dump_dir *dd = dd_opendir(dirname, 0);
                if (dd)
                    dd_delete(dd);
            }
            _exit(r);
        }
        /* No "report-cli/gui" event found, do it old-style */

        path = path1;
        VERB1 log("Executing: %s", path);
        execv(path, args);
        /* Did not find the desired executable in the installation directory.
         * Trying to find it in PATH.
         */
        path = path2;
        execvp(path, args);
        perror_msg_and_die("Can't execute %s", path);
    }

    /* parent */
    free(prgname);

    if (!(flags & LIBREPORT_WAIT) && (flags & LIBREPORT_GETPID))
        return pid;

    /* we are here either if LIBREPORT_WAIT (caller wants exitcode)
     * or !LIBREPORT_GETPID (caller doesn't want to have a child).
     * In both cases, we need to wait for child:
     */
    int status;
    pid = safe_waitpid(pid, &status, 0);
    if (pid <= 0)
    {
        perror_msg("waitpid");
        return -1;
    }

    if (WIFEXITED(status))
    {
        VERB2 log("reporting finished with exitcode %d", WEXITSTATUS(status));
        return WEXITSTATUS(status);
    }
    /* child died from a signal: WIFSIGNALED(status) should be true */
    VERB2 log("reporting killed by signal %d", WTERMSIG(status));
    return WTERMSIG(status) + 128;
}