Example #1
0
static int create_and_upload_archive(
                const char *dump_dir_name,
                const char *url,
                map_string_t *settings,
                char **remote_name)
{
    int result = 1; /* error */
    char* tempfile = NULL;

    /* Create a child gzip which will compress the data */
    /* SELinux guys are not happy with /tmp, using /var/run/abrt */
    /* Reverted back to /tmp for ABRT2 */
    /* Changed again to /var/tmp because of Fedora feature tmp-on-tmpfs */
    tempfile = concat_path_basename(LARGE_DATA_TMP_DIR, dump_dir_name);
    tempfile = append_to_malloced_string(tempfile, ".tar.gz");

    string_vector_ptr_t exclude_from_report = get_global_always_excluded_elements();

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        xfunc_die(); /* error msg is already logged by dd_opendir */

    /* Compressing e.g. 0.5gig coredump takes a while. Let client know what we are doing */
    log_warning(_("Compressing data"));
    if (dd_create_archive(dd, tempfile, (const_string_vector_const_ptr_t)exclude_from_report, 0) != 0)
    {
        log_error("Can't create temporary file in %s", LARGE_DATA_TMP_DIR);
        goto ret;
    }

    dd_close(dd);
    dd = NULL;

    /* Upload the archive */
    /* Upload from /tmp to /tmp + deletion -> BAD, exclude this possibility */
    if (url && url[0] && strcmp(url, "file://"LARGE_DATA_TMP_DIR"/") != 0)
        result = interactive_upload_file(url, tempfile, settings, remote_name);
    else
    {
        result = 0; /* success */
        log_warning(_("Archive is created: '%s'"), tempfile);
        *remote_name = tempfile;
        tempfile = NULL;
    }

 ret:
    dd_close(dd);

    if (tempfile)
    {
        unlink(tempfile);
        free(tempfile);
    }

    return result;
}
Example #2
0
int
main(int argc, char *argv[])
{
	int ch;

	setprogname(argv[0]);
	(void)setlocale(LC_ALL, "");

	while ((ch = getopt(argc, argv, "")) != -1) {
		switch (ch) {
		default:
			errx(EXIT_FAILURE, "usage: dd [operand ...]");
			/* NOTREACHED */
		}
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	jcl(argv);
	setup();

	(void)signal(SIGINFO, summaryx);
	(void)signal(SIGINT, terminate);
	(void)sigemptyset(&infoset);
	(void)sigaddset(&infoset, SIGINFO);

	(void)atexit(summary);

	while (files_cnt--)
		dd_in();

	dd_close();
	exit(0);
	/* NOTREACHED */
}
Example #3
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;
}
Example #4
0
static bool problem_info_ensure_writable(problem_info_t *pi)
{
    if (pi->is_writable)
        return true;

    /* chown the directory in any case, because kernel oopses are not foreign */
    /* but their dump directories are not writable without chowning them or */
    /* stealing them. The stealing is deprecated as it breaks the local */
    /* duplicate search and root cannot see them */
    const int res = chown_dir_over_dbus(problem_info_get_dir(pi));
    if (pi->foreign && res != 0)
    {
        error_msg(_("Can't take ownership of '%s'"), problem_info_get_dir(pi));
        return false;
    }
    pi->foreign = false;

    struct dump_dir *dd = open_directory_for_writing(problem_info_get_dir(pi), /* don't ask */ NULL);
    if (!dd)
    {
        error_msg(_("Can't open directory for writing '%s'"), problem_info_get_dir(pi));
        return false;
    }

    problem_info_set_dir(pi, dd->dd_dirname);
    pi->is_writable = true;
    dd_close(dd);
    return true;
}
Example #5
0
int
dd_main(int argc, char *argv[])
{
	int ch;

	while ((ch = getopt(argc, argv, "")) != -1) {
		switch (ch) {
		default:
			fprintf(stderr, "usage: dd [operand ...]\n");
			exit(1);
			/* NOTREACHED */
		}
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	jcl(argv);
	setup();

//	(void)signal(SIGINFO, summaryx);
	(void)signal(SIGINT, terminate);
	(void)sigemptyset(&infoset);
//	(void)sigaddset(&infoset, SIGINFO);

	(void)atexit(summary);

	while (files_cnt--)
		dd_in();

	dd_close();
	exit(0);
	/* NOTREACHED */
}
Example #6
0
bool ignored_problems_contains(ignored_problems_t *set, const char *problem_id)
{
    struct dump_dir *dd = dd_opendir(problem_id, IGN_DD_OPEN_FLAGS);
    if (!dd)
    {
        /* We do not consider this as an error because the directory can be
         * deleted by other programs. This code expects that dd_opendir()
         * already emitted good and explanatory message. This message attempts
         * to explain what the previous failure causes.
         */
        VERB1 error_msg("Can't open '%s'."
                " Won't try to check whether it belongs to ignored problems",
                problem_id);
        return false;
    }
    char *uuid = dd_load_text_ext(dd, FILENAME_UUID, IGN_DD_LOAD_TEXT_FLAGS);
    char *duphash = dd_load_text_ext(dd, FILENAME_DUPHASH, IGN_DD_LOAD_TEXT_FLAGS);
    dd_close(dd);

    log_notice("Going to check if problem '%s' is in ignored problems '%s'",
            problem_id, set->ign_set_file_path);

    bool found = ignored_problems_file_contains(set, problem_id, uuid, duphash,
                    /* (FILE **) */NULL, "r");

    free(duphash);
    free(uuid);

    return found;
}
Example #7
0
void ignored_problems_remove(ignored_problems_t *set, const char *problem_id)
{
    char *uuid = NULL;
    char *duphash = NULL;
    struct dump_dir *dd = dd_opendir(problem_id, IGN_DD_OPEN_FLAGS);
    if (dd)
    {
        uuid = dd_load_text_ext(dd, FILENAME_UUID, IGN_DD_LOAD_TEXT_FLAGS);
        duphash = dd_load_text_ext(dd, FILENAME_DUPHASH, IGN_DD_LOAD_TEXT_FLAGS);
        dd_close(dd);
    }
    else
    {
        /* We do not consider this as an error because the directory can be
         * deleted by other programs. This code expects that dd_opendir()
         * already emitted good explanatory message. This message
         * explains what the previous failure causes.
         */
        VERB1 error_msg("Can't get UUID/DUPHASH from"
                " '%s' to remove it from the ignored problems:"
                " can't open the problem", problem_id);
    }

    ignored_problems_remove_row(set, problem_id, uuid, duphash);

    free(duphash);
    free(uuid);
}
/*
 ***************************************************************************
 ********[ The Main ]********************************************************
 *************************************************************************** */
int main(int argv, char *argc[])
{
	if (argv == 1)
	{
		printf("need to run from DayDream!\n");
		exit(1);
	}

	d = dd_initdoor(argc[1]);

	dd_getstrval(d, username, USER_HANDLE);
	dd_getstrval(d, buf, DOOR_PARAMS);

	Unzipper();     /* unzip & get filesize */
	DataBitch();    /* open fastest database */
	Me_Search();    /* search for user record */

	if (newuser == 1)
		WriteNewbie();           /* if we are newbie */
	if (newuser == 0)
		My_Bytes();              /* if we do have bytes */

	fclose(datafile); /* close files */
	unlink(DizzyBuf);
	unlink(DizzyBuf);
	dd_close(d);
	return 0;
}
/* unzip and get filesize */
int Unzipper()
{
	FILE *FileName; /* uploaded filename */
	FILE *checkDiz; /* file_id.diz temp'o */

	sprintf(cmdline, "unzip -o %s FILE_ID.DIZ file_id.diz -d /home/bbs/doors >/dev/null", buf);

	system(cmdline);

	strcpy(DizzyBuf, "/home/bbs/doors/file_id.diz");

	checkDiz = fopen("/home/bbs/doors/file_id.diz", "rt");
	if (!checkDiz)
	{
		checkDiz = fopen("/home/bbs/doors/FILE_ID.DIZ", "rt");
		if (!checkDiz)
		{
			printf("no f*****g DIZZY here, lame zip :)\n");
			dd_close(d);
			exit(1);
		}
		strcpy(DizzyBuf, "/home/bbs/doors/FILE_ID.DIZ");
	}

	DizzyCompute();         /* dizzy date calculation */

	FileName = fopen(buf, "r");   /* open the parameter filename */
	fseek(FileName, 0, SEEK_END); /* zoom to end */
	FileSize = 0;                 /* make sure the size is zero */
	FileSize = ftell(FileName);   /* grab the position (filesize) */
	fclose(FileName);             /* close the filen */

	return 0;
}
Example #10
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 */
}
Example #11
0
/* The function expects that FILENAME_COUNT dump dir element is created by
 * abrtd after all post-create events are successfully done. Thus if
 * FILENAME_COUNT element doesn't exist abrtd can consider the dump directory
 * as unprocessed.
 *
 * Relying on content of dump directory has one problem. If a hook provides
 * FILENAME_COUNT abrtd will consider the dump directory as processed.
 */
static void mark_unprocessed_dump_dirs_not_reportable(const char *path)
{
    log_notice("Searching for unprocessed dump directories");

    DIR *dp = opendir(path);
    if (!dp)
    {
        perror_msg("Can't open directory '%s'", path);
        return;
    }

    struct dirent *dent;
    while ((dent = readdir(dp)) != NULL)
    {
        if (dot_or_dotdot(dent->d_name))
            continue; /* skip "." and ".." */

        char *full_name = concat_path_file(path, dent->d_name);

        struct stat stat_buf;
        if (stat(full_name, &stat_buf) != 0)
        {
            perror_msg("Can't access path '%s'", full_name);
            goto next_dd;
        }

        if (S_ISDIR(stat_buf.st_mode) == 0)
            /* This is expected. The dump location contains some aux files */
            goto next_dd;

        struct dump_dir *dd = dd_opendir(full_name, /*flags*/0);
        if (dd)
        {
            if (!problem_dump_dir_is_complete(dd) && !dd_exist(dd, FILENAME_NOT_REPORTABLE))
            {
                log_warning("Marking '%s' not reportable (no '"FILENAME_COUNT"' item)", full_name);

                dd_save_text(dd, FILENAME_NOT_REPORTABLE, _("The problem data are "
                            "incomplete. This usually happens when a problem "
                            "is detected while computer is shutting down or "
                            "user is logging out. In order to provide "
                            "valuable problem reports, ABRT will not allow "
                            "you to submit this problem. If you have time and "
                            "want to help the developers in their effort to "
                            "sort out this problem, please contact them directly."));

            }
            dd_close(dd);
        }

  next_dd:
        free(full_name);
    }
    closedir(dp);
}
int main(int argc, char **argv)
{
    /* I18n */
    setlocale(LC_ALL, "");
#if ENABLE_NLS
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

    abrt_init(argv);

    const char *dump_dir_name = ".";
    const char *root_dir = NULL;

    /* Can't keep these strings/structs static: _() doesn't support that */
    const char *program_usage_string = _(
        "& [-v] -d DIR\n"
        "\n"
        "Save container metadata"
    );
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1,
    };
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT_STRING('d', NULL, &dump_dir_name, "DIR"     , _("Problem directory")),
        OPT_STRING('r', NULL, &root_dir,      "ROOTDIR" , _("Root directory for running container commands")),
        OPT_END()
    };
    /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);

    export_abrt_envvars(0);

    struct dump_dir *dd = dd_opendir(dump_dir_name, /* for writing */0);
    if (dd == NULL)
        xfunc_die();

    char *container_cmdline = dd_load_text_ext(dd, FILENAME_CONTAINER_CMDLINE, DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
    if (container_cmdline == NULL)
        error_msg_and_die("The crash didn't occur in container");

    if (strstr("/docker ", container_cmdline) == 0)
        dump_docker_info(dd, root_dir);
    else if (strstr("/lxc-", container_cmdline) == 0)
        dump_lxc_info(dd, container_cmdline);
    else
        error_msg_and_die("Unsupported container technology");

    free(container_cmdline);
    dd_close(dd);

    return 0;
}
Example #13
0
problem_data_t *create_problem_data_for_reporting(const char *dump_dir_name)
{
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return NULL; /* dd_opendir already emitted error msg */
    char **exclude_items = build_exclude_vector(getenv("EXCLUDE_FROM_REPORT"));
    problem_data_t *problem_data = problem_data_new();
    problem_data_load_from_dump_dir(problem_data, dd, exclude_items);
    dd_close(dd);
    free(exclude_items);
    return problem_data;
}
Example #14
0
char* problem_data_save(problem_data_t *pd)
{
    load_abrt_conf();

    struct dump_dir *dd = create_dump_dir_from_problem_data_ext(pd, g_settings_dump_location, /*fs owner*/0);

    char *problem_id = NULL;
    if (dd)
    {
        problem_id = xstrdup(dd->dd_dirname);
        dd_close(dd);
    }

    log_info("problem id: '%s'", problem_id);
    return problem_id;
}
Example #15
0
static bool this_is_a_dd(const char *dirname)
{
    /* Prevent get_dirsize_find_largest_dir() from flooding log
     * with "is not a problem directory" messages
     * if there are stray dirs in /var/spool/abrt:
     */
    int sv_logmode = logmode;
    logmode = 0;

    struct dump_dir *dd = dd_opendir(dirname,
                /*flags:*/ DD_OPEN_READONLY | DD_FAIL_QUIETLY_ENOENT | DD_FAIL_QUIETLY_EACCES
    );
    dd_close(dd);

    logmode = sv_logmode;
    return dd != NULL;
}
Example #16
0
int
main(int argc, char *argv[])
{
	(void)setlocale(LC_CTYPE, "");
	jcl(argv);
	setup();

	(void)signal(SIGINFO, summaryx);
	(void)signal(SIGINT, terminate);

	atexit(summary);

	while (files_cnt--)
		dd_in();

	dd_close();
	exit(0);
}
Example #17
0
File: dd.c Project: a565109863/src
int
main(int argc, char *argv[])
{
	jcl(argv);
	setup();

	(void)signal(SIGINFO, summaryx);
	(void)signal(SIGINT, terminate);

	atexit(summary);

	if (cpy_cnt != (size_t)-1) {
		while (files_cnt--)
			dd_in();
	}

	dd_close();
	exit(0);
}
Example #18
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 */
}
Example #19
0
static problem_data_t *FillCrashInfo(const char *dump_dir_name)
{
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return NULL;

    problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
//Not needed anymore?
//    char *events = list_possible_events(dd, NULL, "");
    dd_close(dd);
//
//    add_to_problem_data_ext(problem_data, CD_EVENTS, events,
//                          CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
//    free(events);

    add_to_problem_data_ext(problem_data, CD_DUMPDIR, dump_dir_name,
                          CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);

    return problem_data;
}
Example #20
0
void problem_data_reload_from_dump_dir(void)
{
    free(g_events);

    struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY);
    if (!dd)
        xfunc_die(); /* dd_opendir already logged error msg */

    problem_data_t *new_cd = create_problem_data_from_dump_dir(dd);
    problem_data_add_text_noteditable(new_cd, CD_DUMPDIR, g_dump_dir_name);

    g_events = list_possible_events(dd, NULL, "");
    dd_close(dd);

    /* Copy "selected for reporting" flags */
    GHashTableIter iter;
    char *name;
    struct problem_item *new_item;
    g_hash_table_iter_init(&iter, new_cd);
    while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&new_item))
    {
        struct problem_item *old_item = g_cd ? problem_data_get_item_or_NULL(g_cd, name) : NULL;
        if (old_item)
        {
            new_item->selected_by_user = old_item->selected_by_user;
            new_item->allowed_by_reporter = old_item->allowed_by_reporter;
            new_item->default_by_reporter = old_item->default_by_reporter;
            new_item->required_by_reporter = old_item->required_by_reporter;
        }
        else
        {
            new_item->selected_by_user = 0;
            new_item->allowed_by_reporter = 0;
            new_item->default_by_reporter = 0;
            new_item->required_by_reporter = 0;
        }
        //log("%s: was ->selected_by_user=%d", __func__, new_item->selected_by_user);
    }
    problem_data_free(g_cd);
    g_cd = new_cd;
}
Example #21
0
static void save_bt_to_dump_dir(const char *bt, const char *exe, const char *reason)
{
    time_t t = time(NULL);
    const char *iso_date = iso_date_string(&t);

    pid_t my_pid = getpid();

    char base[sizeof("xorg-YYYY-MM-DD-hh:mm:ss-%lu-%lu") + 2 * sizeof(long)*3];
    sprintf(base, "xorg-%s-%lu-%u", iso_date, (long)my_pid, g_bt_count);
    char *path = concat_path_file(debug_dumps_dir, base);

    struct dump_dir *dd = dd_create(path, /*fs owner*/0, DEFAULT_DUMP_DIR_MODE);
    if (dd)
    {
        dd_create_basic_files(dd, /*no uid*/(uid_t)-1L, NULL);
        dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
        dd_save_text(dd, FILENAME_ANALYZER, "abrt-xorg");
        dd_save_text(dd, FILENAME_TYPE, "xorg");
        dd_save_text(dd, FILENAME_REASON, reason);
        dd_save_text(dd, FILENAME_BACKTRACE, bt);
        /*
         * Reporters usually need component name to file a bug.
	 * It is usually derived from executable.
         * We _guess_ X server's executable name as a last resort.
         * Better ideas?
         */
        if (!exe)
        {
            exe = "/usr/bin/X";
            if (access("/usr/bin/Xorg", X_OK) == 0)
                exe = "/usr/bin/Xorg";
        }
        dd_save_text(dd, FILENAME_EXECUTABLE, exe);
        if (!(g_opts & OPT_x))
            dd_set_no_owner(dd);
        dd_close(dd);
        notify_new_path(path);
    }

    free(path);
}
Example #22
0
GList *list_possible_events_glist(const char *problem_dir_name,
                                  const char *pfx)
{
    struct dump_dir *dd = dd_opendir(problem_dir_name, DD_OPEN_READONLY);
    GList *l = NULL;
    char *events = list_possible_events(dd, problem_dir_name, pfx);
    char *start = events;
    char *end = strchr(events, '\n');

    while(end)
    {
        *end = '\0';
        l = g_list_append(l, xstrdup(start));
        start = end + 1;
        end = strchr(start, '\n');
    }

    dd_close(dd);
    free(events);

    return l;
}
Example #23
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;
}
Example #24
0
void ignored_problems_add(ignored_problems_t *set, const char *problem_id)
{
    struct dump_dir *dd = dd_opendir(problem_id, IGN_DD_OPEN_FLAGS);
    if (!dd)
    {
        /* We do not consider this as an error because the directory can be
         * deleted by other programs. This code expects that dd_opendir()
         * already emitted good explanatory message. This message
         * explains what the previous failure causes.
         */
        VERB1 log("Can't add problem '%s' to ignored problems:"
                " can't open the problem", problem_id);
        return;
    }
    char *uuid = dd_load_text_ext(dd, FILENAME_UUID, IGN_DD_LOAD_TEXT_FLAGS);
    char *duphash = dd_load_text_ext(dd, FILENAME_DUPHASH, IGN_DD_LOAD_TEXT_FLAGS);
    dd_close(dd);

    ignored_problems_add_row(set, problem_id, uuid, duphash);

    free(duphash);
    free(uuid);
}
int main(int argc, char **argv)
{
    /* I18n */
    setlocale(LC_ALL, "");
#if ENABLE_NLS
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

    abrt_init(argv);

    /* Can't keep these strings/structs static: _() doesn't support that */
    const char *program_usage_string = _(
        "& [options] -d DIR\n"
        "\n"
        "Analyzes C/C++ backtrace, generates duplication hash, backtrace rating,\n"
        "and identifies crash function in problem directory DIR"
    );
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1
    };
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Problem directory")),
        OPT_END()
    };
    /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);

    export_abrt_envvars(0);

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

    char *component = dd_load_text(dd, FILENAME_COMPONENT);

    /* Read backtrace */
    char *backtrace_str = dd_load_text_ext(dd, FILENAME_BACKTRACE,
                                           DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
    if (!backtrace_str)
    {
        dd_close(dd);
        return 1;
    }

    /* Compute backtrace hash */
    struct sr_location location;
    sr_location_init(&location);
    const char *backtrace_str_ptr = backtrace_str;
    struct sr_gdb_stacktrace *backtrace = sr_gdb_stacktrace_parse(&backtrace_str_ptr, &location);
    free(backtrace_str);

    /* Store backtrace hash */
    if (!backtrace)
    {
        /*
         * The parser failed. Compute the duphash from the executable
         * instead of a backtrace.
         * and component only.  This is not supposed to happen often.
         */
        log(_("Backtrace parsing failed for %s"), dump_dir_name);
        log("%d:%d: %s", location.line, location.column, location.message);
        struct strbuf *emptybt = strbuf_new();

        char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
        strbuf_prepend_str(emptybt, executable);
        free(executable);

        strbuf_prepend_str(emptybt, component);

        log_debug("Generating duphash: %s", emptybt->buf);
        char hash_str[SHA1_RESULT_LEN*2 + 1];
        str_to_sha1str(hash_str, emptybt->buf);

        dd_save_text(dd, FILENAME_DUPHASH, hash_str);
        /*
         * Other parts of ABRT assume that if no rating is available,
         * it is ok to allow reporting of the bug. To be sure no bad
         * backtrace is reported, rate the backtrace with the lowest
         * rating.
         */
        dd_save_text(dd, FILENAME_RATING, "0");

        strbuf_free(emptybt);
        free(component);
        dd_close(dd);

        /* Report success even if the parser failed, as the backtrace
         * has been created and rated. The failure is caused by a flaw
         * in the parser, not in the backtrace.
         */
        return 0;
    }

    /* Compute duplication hash. */
    struct sr_thread *crash_thread =
        (struct sr_thread *)sr_gdb_stacktrace_find_crash_thread(backtrace);

    if (crash_thread)
    {
        char *hash_str;

        if (g_verbose >= 3)
        {
            hash_str = sr_thread_get_duphash(crash_thread, 3, component,
                                             SR_DUPHASH_NOHASH);
            log("Generating duphash: %s", hash_str);
            free(hash_str);
        }

        hash_str = sr_thread_get_duphash(crash_thread, 3, component,
                                         SR_DUPHASH_NORMAL);
        dd_save_text(dd, FILENAME_DUPHASH, hash_str);
        free(hash_str);
    }
    else
        log(_("Crash thread not found"));


    /* Compute the backtrace rating. */
    float quality = sr_gdb_stacktrace_quality_complex(backtrace);
    const char *rating;
    if (quality < 0.6f)
        rating = "0";
    else if (quality < 0.7f)
        rating = "1";
    else if (quality < 0.8f)
        rating = "2";
    else if (quality < 0.9f)
        rating = "3";
    else
        rating = "4";
    dd_save_text(dd, FILENAME_RATING, rating);

    /* Get the function name from the crash frame. */
    struct sr_gdb_frame *crash_frame = sr_gdb_stacktrace_get_crash_frame(backtrace);
    if (crash_frame)
    {
        if (crash_frame->function_name &&
            0 != strcmp(crash_frame->function_name, "??"))
        {
            dd_save_text(dd, FILENAME_CRASH_FUNCTION, crash_frame->function_name);
        }
        sr_gdb_frame_free(crash_frame);
    }
    sr_gdb_stacktrace_free(backtrace);
    dd_close(dd);
    free(component);
    return 0;
}
Example #26
0
static void report_to_bugzilla(const char *dump_dir_name, map_string_h *settings)
{
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        xfunc_die(); /* dd_opendir already emitted error msg */
    problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
    dd_close(dd);

    const char *env;
    const char *login;
    const char *password;
    const char *bugzilla_xmlrpc;
    const char *bugzilla_url;
    bool ssl_verify;

    env = getenv("Bugzilla_Login");
    login = env ? env : get_map_string_item_or_empty(settings, "Login");
    env = getenv("Bugzilla_Password");
    password = env ? env : get_map_string_item_or_empty(settings, "Password");
    if (!login[0] || !password[0])
        error_msg_and_die(_("Empty login or password, please check your configuration"));

    env = getenv("Bugzilla_BugzillaURL");
    bugzilla_url = env ? env : get_map_string_item_or_empty(settings, "BugzillaURL");
    if (!bugzilla_url[0])
        bugzilla_url = "https://bugzilla.redhat.com";
    bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url);

    env = getenv("Bugzilla_SSLVerify");
    ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify"));

    const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT);
    const char *duphash   = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH);
    if (!duphash)
        error_msg_and_die(_("Essential file '%s' is missing, can't continue.."),
                          FILENAME_DUPHASH);

    if (!*duphash)
        error_msg_and_die(_("Essential file '%s' is empty, can't continue.."),
                          FILENAME_DUPHASH);

    const char *release   = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE);
    if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */
        release = get_problem_item_content_or_NULL(problem_data, "release");

    struct abrt_xmlrpc *client = abrt_xmlrpc_new_client(bugzilla_xmlrpc, ssl_verify);

    log(_("Logging into Bugzilla at %s"), bugzilla_url);
    rhbz_login(client, login, password);

    log(_("Checking for duplicates"));
    char *product = NULL;
    char *version = NULL;
    parse_release_for_bz(release, &product, &version);
    free(version);

    xmlrpc_value *result;
    if (strcmp(product, "Fedora") == 0)
        result  = rhbz_search_duphash(client, component, product, duphash);
    else
        result  = rhbz_search_duphash(client, component, NULL, duphash);

    xmlrpc_value *all_bugs = rhbz_get_member("bugs", result);
    xmlrpc_DECREF(result);

    if (!all_bugs)
        error_msg_and_die(_("Missing mandatory member 'bugs'"));

    int all_bugs_size = rhbz_array_size(all_bugs);
    // When someone clones bug it has same duphash, so we can find more than 1.
    // Need to be checked if component is same.
    VERB3 log("Bugzilla has %i reports with same duphash '%s'",
              all_bugs_size, duphash);

    int bug_id = -1, dependent_bug = -1;
    struct bug_info *bz = NULL;
    if (all_bugs_size > 0)
    {
        bug_id = rhbz_bug_id(all_bugs);
        xmlrpc_DECREF(all_bugs);
        bz = rhbz_bug_info(client, bug_id);

        if (strcmp(bz->bi_product, product) != 0)
        {
            dependent_bug = bug_id;
            /* found something, but its a different product */
            free_bug_info(bz);

            xmlrpc_value *result = rhbz_search_duphash(client, component,
                                                       product, duphash);
            xmlrpc_value *all_bugs = rhbz_get_member("bugs", result);
            xmlrpc_DECREF(result);

            all_bugs_size = rhbz_array_size(all_bugs);
            if (all_bugs_size > 0)
            {
                bug_id = rhbz_bug_id(all_bugs);
                bz = rhbz_bug_info(client, bug_id);
            }
            xmlrpc_DECREF(all_bugs);
        }

    }
    free(product);

    if (all_bugs_size == 0) // Create new bug
    {
        log(_("Creating a new bug"));
        bug_id = rhbz_new_bug(client, problem_data, bug_id);

        log("Adding attachments to bug %i", bug_id);
        char bug_id_str[sizeof(int)*3 + 2];
        sprintf(bug_id_str, "%i", bug_id);

        rhbz_attachments(client, bug_id_str, problem_data);

        log(_("Logging out"));
        rhbz_logout(client);

        log("Status: NEW %s/show_bug.cgi?id=%u", bugzilla_url, bug_id);
        abrt_xmlrpc_free_client(client);
        return;
    }

    // decision based on state
    log(_("Bug is already reported: %i"), bz->bi_id);
    if ((strcmp(bz->bi_status, "CLOSED") == 0)
        && (strcmp(bz->bi_resolution, "DUPLICATE") == 0))
    {
        struct bug_info *origin;
        origin = rhbz_find_origin_bug_closed_duplicate(client, bz);
        if (origin)
        {
            free_bug_info(bz);
            bz = origin;
        }
    }

    if (strcmp(bz->bi_status, "CLOSED") != 0)
    {
        if ((strcmp(bz->bi_reporter, login) != 0)
            && (!g_list_find_custom(bz->bi_cc_list, login, (GCompareFunc)g_strcmp0)))
        {
            log(_("Add %s to CC list"), login);
            rhbz_mail_to_cc(client, bz->bi_id, login);
        }

        char *dsc = make_description_comment(problem_data);
        if (dsc)
        {
            const char *package = get_problem_item_content_or_NULL(problem_data,
                                                                   FILENAME_PACKAGE);
            const char *release = get_problem_item_content_or_NULL(problem_data,
                                                                   FILENAME_OS_RELEASE);
            if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */
                release = get_problem_item_content_or_NULL(problem_data, "release");
            const char *arch = get_problem_item_content_or_NULL(problem_data,
                                                                FILENAME_ARCHITECTURE);
            const char *is_private = get_problem_item_content_or_NULL(problem_data,
                                                                      "is_private");

            char *full_dsc = xasprintf("Package: %s\n"
                                       "Architecture: %s\n"
                                       "OS Release: %s\n"
                                       "%s", package, arch, release, dsc);

            log(_("Adding new comment to bug %d"), bz->bi_id);
            free(dsc);

            int is_priv = is_private && string_to_bool(is_private);
            rhbz_add_comment(client, bz->bi_id, full_dsc, is_priv);
            free(full_dsc);
        }
    }

    log(_("Logging out"));
    rhbz_logout(client);

    log("Status: %s%s%s %s/show_bug.cgi?id=%u",
                bz->bi_status,
                bz->bi_resolution ? " " : "",
                bz->bi_resolution ? bz->bi_resolution : "",
                bugzilla_url,
                bz->bi_id);

    dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (dd)
    {
        char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", bugzilla_url, bz->bi_id);
        add_reported_to(dd, msg);
        free(msg);
        dd_close(dd);
    }

    free_problem_data(problem_data);
    free_bug_info(bz);
    abrt_xmlrpc_free_client(client);
}
Example #27
0
int main(int argc, char **argv)
{
    abrt_init(argv);
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1,
        OPT_g = 1 << 2,
        OPT_b = 1 << 3,
        OPT_u = 1 << 4,
        OPT_r = 1 << 5,
    };

    const char *bugs = NULL, *release = NULL, *dump_dir_path = ".";
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT__DUMP_DIR(&dump_dir_path),
        OPT_GROUP(""),
        OPT_STRING('b', "bugs", &bugs, "ID1[,ID2,...]" , _("List of bug ids")),
        OPT_STRING('u', "url", &bodhi_url, "URL", _("Specify a bodhi server url")),
        OPT_OPTSTRING('r', "release", &release, "RELEASE", _("Specify a release")),
        OPT_END()
    };

    const char *program_usage_string = _(
        "& [-v] [-r[RELEASE]] (-b ID1[,ID2,...] | PKG-NAME) [PKG-NAME]... \n"
        "\n"
        "Search for updates on bodhi server"
    );

    unsigned opts =  parse_opts(argc, argv, program_options, program_usage_string);

    if (!bugs && !argv[optind])
        show_usage_and_die(program_usage_string, program_options);

    struct strbuf *query = strbuf_new();
    if (bugs)
        query = strbuf_append_strf(query, "bugs=%s&", bugs);

    if (opts & OPT_r)
    {
        if (release)
        {
            query = strbuf_append_strf(query, "release=%s&", release);
        }
        else
        {
            struct dump_dir *dd = dd_opendir(dump_dir_path, DD_OPEN_READONLY);
            if (!dd)
                xfunc_die();

            problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
            dd_close(dd);
            if (!problem_data)
                xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */

            char *product, *version;
            map_string_t *osinfo = new_map_string();
            problem_data_get_osinfo(problem_data, osinfo);
            parse_osinfo_for_rhts(osinfo, &product, &version);
            query = strbuf_append_strf(query, "release=f%s&", version);
            free(product);
            free(version);
            free_map_string(osinfo);
        }
    }

    if (argv[optind])
    {
        char *escaped = g_uri_escape_string(argv[optind], NULL, 0);
        query = strbuf_append_strf(query, "package=%s&", escaped);
        free(escaped);
    }

    if (query->buf[query->len - 1] == '&')
        query->buf[query->len - 1] = '\0';

    log(_("Searching for updates"));
    GHashTable *update_hash_tbl = bodhi_query_list(query->buf, release);
    strbuf_free(query);

    if (!update_hash_tbl || !g_hash_table_size(update_hash_tbl))
    {
        log(_("No updates for this package found"));
        /*if (update_hash_tbl) g_hash_table_unref(update_hash_tbl);*/
        return 0;
    }

    GHashTableIter iter;
    char *name;
    struct bodhi *b;
    struct strbuf *q = strbuf_new();
    g_hash_table_iter_init(&iter, update_hash_tbl);
    while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &b))
    {
        char *installed_pkg_nvr = rpm_get_nvr_by_pkg_name(name);
        if (installed_pkg_nvr && rpmvercmp(installed_pkg_nvr, b->nvr) >= 0)
        {
            log_info("Update %s is older or same as local version %s, skipping", b->nvr, installed_pkg_nvr);
            free(installed_pkg_nvr);
            continue;
        }
        free(installed_pkg_nvr);

        strbuf_append_strf(q, " %s", b->nvr);
    }

    /*g_hash_table_unref(update_hash_tbl);*/

    if (!q->len)
    {
        /*strbuf_free(q);*/
        log(_("Local version of the package is newer than available updates"));
        return 0;
    }

    /* Message is split into text and command in order to make
     * translator's job easier
     */

    /* We suggest the command which is most likely to exist on user's system,
     * and which is familiar to the largest population of users.
     * There are other tools (pkcon et al) which might be somewhat more
     * convenient (for example, they might be usable from non-root), but they
     * might be not present on the system, may evolve or be superseded,
     * while yum is unlikely to do so.
     */
    strbuf_prepend_str(q, "yum update --enablerepo=fedora --enablerepo=updates-testing");

    char *msg = xasprintf(_("An update exists which might fix your problem. "
                "You can install it by running: %s. "
                "Do you want to continue with reporting the bug?"),
                q->buf
    );
    /*strbuf_free(q);*/

    return !ask_yes_no(msg);
}
Example #28
0
int main(int argc, char **argv)
{
    abrt_init(argv);

    const char *dump_dir_name = ".";

    /* Can't keep these strings/structs static: _() doesn't support that */
    const char *program_usage_string = _(
        "\b [-v] -d DIR\n"
        "\n"
        "Calculates and saves UUID and DUPHASH of python crash dumps"
    );
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1,
    };
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Dump directory")),
        OPT_END()
    };
    /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);

    export_abrt_envvars(0);

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return 1;
    char *bt = dd_load_text(dd, FILENAME_BACKTRACE);

    /* Hash 1st line of backtrace and save it as UUID and DUPHASH */
    /* "example.py:1:<module>:ZeroDivisionError: integer division or modulo by zero" */

    unsigned char hash_bytes[SHA1_RESULT_LEN];
    sha1_ctx_t sha1ctx;
    sha1_begin(&sha1ctx);
    const char *bt_end = strchrnul(bt, '\n');
    sha1_hash(&sha1ctx, bt, bt_end - bt);
    sha1_end(&sha1ctx, hash_bytes);
    free(bt);

    char hash_str[SHA1_RESULT_LEN*2 + 1];
    unsigned len = SHA1_RESULT_LEN;
    unsigned char *s = hash_bytes;
    char *d = hash_str;
    while (len)
    {
        *d++ = "0123456789abcdef"[*s >> 4];
        *d++ = "0123456789abcdef"[*s & 0xf];
        s++;
        len--;
    }
    *d = '\0';

    dd_save_text(dd, FILENAME_UUID, hash_str);
    dd_save_text(dd, FILENAME_DUPHASH, hash_str);
    dd_close(dd);

    return 0;
}
Example #29
0
/* Create a new problem directory from client session.
 * Caller must ensure that all fields in struct client
 * are properly filled.
 */
static int create_problem_dir(GHashTable *problem_info, unsigned pid)
{
    /* Exit if free space is less than 1/4 of MaxCrashReportsSize */
    if (g_settings_nMaxCrashReportsSize > 0)
    {
        if (low_free_space(g_settings_nMaxCrashReportsSize, g_settings_dump_location))
            exit(1);
    }

    /* Create temp directory with the problem data.
     * This directory is renamed to final directory name after
     * all files have been stored into it.
     */

    gchar *dir_basename = g_hash_table_lookup(problem_info, "basename");
    if (!dir_basename)
        dir_basename = g_hash_table_lookup(problem_info, FILENAME_TYPE);

    char *path = xasprintf("%s/%s-%s-%u.new",
                           g_settings_dump_location,
                           dir_basename,
                           iso_date_string(NULL),
                           pid);

    /* This item is useless, don't save it */
    g_hash_table_remove(problem_info, "basename");

    /* No need to check the path length, as all variables used are limited,
     * and dd_create() fails if the path is too long.
     */
    struct dump_dir *dd = dd_create(path, /*fs owner*/0, DEFAULT_DUMP_DIR_MODE);
    if (!dd)
    {
        error_msg_and_die("Error creating problem directory '%s'", path);
    }

    dd_create_basic_files(dd, client_uid, NULL);
    dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);

    gpointer gpkey = g_hash_table_lookup(problem_info, FILENAME_CMDLINE);
    if (!gpkey)
    {
        /* Obtain and save the command line. */
        char *cmdline = get_cmdline(pid);
        if (cmdline)
        {
            dd_save_text(dd, FILENAME_CMDLINE, cmdline);
            free(cmdline);
        }
    }

    /* Store id of the user whose application crashed. */
    char uid_str[sizeof(long) * 3 + 2];
    sprintf(uid_str, "%lu", (long)client_uid);
    dd_save_text(dd, FILENAME_UID, uid_str);

    GHashTableIter iter;
    gpointer gpvalue;
    g_hash_table_iter_init(&iter, problem_info);
    while (g_hash_table_iter_next(&iter, &gpkey, &gpvalue))
    {
        dd_save_text(dd, (gchar *) gpkey, (gchar *) gpvalue);
    }

    dd_close(dd);

    /* Not needing it anymore */
    g_hash_table_destroy(problem_info);

    /* Move the completely created problem directory
     * to final directory.
     */
    char *newpath = xstrndup(path, strlen(path) - strlen(".new"));
    if (rename(path, newpath) == 0)
        strcpy(path, newpath);
    free(newpath);

    log_notice("Saved problem directory of pid %u to '%s'", pid, path);

    /* We let the peer know that problem dir was created successfully
     * _before_ we run potentially long-running post-create.
     */
    printf("HTTP/1.1 201 Created\r\n\r\n");
    fflush(NULL);
    close(STDOUT_FILENO);
    xdup2(STDERR_FILENO, STDOUT_FILENO); /* paranoia: don't leave stdout fd closed */

    /* Trim old problem directories if necessary */
    if (g_settings_nMaxCrashReportsSize > 0)
    {
        trim_problem_dirs(g_settings_dump_location, g_settings_nMaxCrashReportsSize * (double)(1024*1024), path);
    }

    run_post_create(path);

    /* free(path); */
    exit(0);
}
Example #30
0
static void create_and_send_email(
                const char *dump_dir_name,
                map_string_h *settings)
{
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        exit(1); /* error msg is already logged by dd_opendir */

    problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
    dd_close(dd);

    char* env;
    env = getenv("Mailx_Subject");
    const char *subject = (env ? env : get_map_string_item_or_NULL(settings, "Subject") ? : "[abrt] full crash report");
    env = getenv("Mailx_EmailFrom");
    const char *email_from = (env ? env : get_map_string_item_or_NULL(settings, "EmailFrom") ? : "user@localhost");
    env = getenv("Mailx_EmailTo");
    const char *email_to = (env ? env : get_map_string_item_or_NULL(settings, "EmailTo") ? : "root@localhost");
    env = getenv("Mailx_SendBinaryData");
    bool send_binary_data = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SendBinaryData"));

    char **args = NULL;
    unsigned arg_size = 0;
    args = append_str_to_vector(args, &arg_size, "/bin/mailx");

    char *dsc = make_description_mailx(problem_data);

    if (send_binary_data)
    {
        GHashTableIter iter;
        char *name;
        struct problem_item *value;
        g_hash_table_iter_init(&iter, problem_data);
        while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
        {
            if (value->flags & CD_FLAG_BIN)
            {
                args = append_str_to_vector(args, &arg_size, "-a");
                args = append_str_to_vector(args, &arg_size, value->content);
            }
        }
    }

    args = append_str_to_vector(args, &arg_size, "-s");
    args = append_str_to_vector(args, &arg_size, subject);
    args = append_str_to_vector(args, &arg_size, "-r");
    args = append_str_to_vector(args, &arg_size, email_from);
    args = append_str_to_vector(args, &arg_size, email_to);

    log(_("Sending an email..."));
    exec_and_feed_input(dsc, args);

    free(dsc);

    while (*args)
        free(*args++);
    args -= arg_size;
    free(args);

    free_problem_data(problem_data);

    dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (dd)
    {
        char *msg = xasprintf("email: %s", email_to);
        add_reported_to(dd, msg);
        free(msg);
        dd_close(dd);
    }
    log("Email was sent to: %s", email_to);
}