void dump_lxc_info(struct dump_dir *dd, const char *lxc_cmd) { if (!dd_exist(dd, FILENAME_CONTAINER)) dd_save_text(dd, FILENAME_CONTAINER, "lxc"); char *mntnf_path = concat_path_file(dd->dd_dirname, FILENAME_MOUNTINFO); FILE *mntnf_file = fopen(mntnf_path, "r"); free(mntnf_path); struct mountinfo mntnf; int r = get_mountinfo_for_mount_point(mntnf_file, &mntnf, "/"); fclose(mntnf_file); if (r != 0) { error_msg("lxc processes must have re-mounted root"); goto dump_lxc_info_cleanup; } const char *mnt_root = MOUNTINFO_ROOT(mntnf); const char *last_slash = strrchr(mnt_root, '/'); if (last_slash == NULL || (strcmp("rootfs", last_slash +1) != 0)) { error_msg("Invalid root path '%s'", mnt_root); goto dump_lxc_info_cleanup; } if (last_slash == mnt_root) { error_msg("root path misses container id: '%s'", mnt_root); goto dump_lxc_info_cleanup; } const char *tmp = strrchr(last_slash - 1, '/'); if (tmp == NULL) { error_msg("root path misses first /: '%s'", mnt_root); goto dump_lxc_info_cleanup; } char *container_id = xstrndup(tmp + 1, (last_slash - tmp) - 1); dd_save_text(dd, FILENAME_CONTAINER_ID, container_id); dd_save_text(dd, FILENAME_CONTAINER_UUID, container_id); free(container_id); /* TODO: make a copy of 'config' */ /* get mount point for MOUNTINFO_MOUNT_SOURCE(mntnf) + MOUNTINFO_ROOT(mntnf) */ dump_lxc_info_cleanup: mountinfo_destroy(&mntnf); }
/* 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); }
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); }
/* 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); }
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; }
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 = "."; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [-v] -d DIR\n" "\n" "Calculates and saves UUID of coredump 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); char *unstrip_n_output = NULL; char *coredump_path = xasprintf("%s/"FILENAME_COREDUMP, dump_dir_name); if (access(coredump_path, R_OK) == 0) unstrip_n_output = run_unstrip_n(dump_dir_name, /*timeout_sec:*/ 30); free(coredump_path); if (unstrip_n_output) { /* Run unstrip -n and trim its output, leaving only sizes and build ids */ /* modifies unstrip_n_output in-place: */ trim_unstrip_output(unstrip_n_output, unstrip_n_output); } else { /* bad dump_dir_name, can't run unstrip, etc... * or maybe missing coredump - try generating it from core_backtrace */ unstrip_n_output = build_ids_from_core_backtrace(dump_dir_name); } /* Hash package + executable + unstrip_n_output and save it as UUID */ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return 1; char *executable = dd_load_text(dd, FILENAME_EXECUTABLE); /* FILENAME_PACKAGE may be missing if ProcessUnpackaged = yes... */ char *package = dd_load_text_ext(dd, FILENAME_PACKAGE, DD_FAIL_QUIETLY_ENOENT); /* Package variable has "firefox-3.5.6-1.fc11[.1]" format */ /* Remove distro suffix and maybe least significant version number */ char *p = package; while (*p) { if (*p == '.' && (p[1] < '0' || p[1] > '9')) { /* We found "XXXX.nondigitXXXX", trim this part */ *p = '\0'; break; } p++; } char *first_dot = strchr(package, '.'); if (first_dot) { char *last_dot = strrchr(first_dot, '.'); if (last_dot != first_dot) { /* There are more than one dot: "1.2.3" * Strip last part, we don't want to distinquish crashes * in packages which differ only by minor release number. */ *last_dot = '\0'; } } char *string_to_hash = xasprintf("%s%s%s", package, executable, unstrip_n_output); /*free(package);*/ /*free(executable);*/ /*free(unstrip_n_output);*/ log_debug("String to hash: %s", string_to_hash); char hash_str[SHA1_RESULT_LEN*2 + 1]; str_to_sha1str(hash_str, string_to_hash); dd_save_text(dd, FILENAME_UUID, hash_str); dd_close(dd); return 0; }
struct dump_dir *create_dump_dir_from_problem_data(problem_data_t *problem_data, const char *base_dir_name) { INITIALIZE_LIBREPORT(); char *type = problem_data_get_content_or_NULL(problem_data, FILENAME_ANALYZER); if (!type) { error_msg(_("Missing required item: '%s'"), FILENAME_ANALYZER); return NULL; } uid_t uid = (uid_t)-1L; char *uid_str = problem_data_get_content_or_NULL(problem_data, FILENAME_UID); if (uid_str) { char *endptr; errno = 0; long val = strtol(uid_str, &endptr, 10); if (errno != 0 || endptr == uid_str || *endptr != '\0' || INT_MAX < val) { error_msg(_("uid value is not valid: '%s'"), uid_str); return NULL; } uid = (uid_t)val; } struct timeval tv; if (gettimeofday(&tv, NULL) < 0) { perror_msg("gettimeofday()"); return NULL; } char *problem_id = xasprintf("%s-%s.%ld-%lu"NEW_PD_SUFFIX, type, iso_date_string(&(tv.tv_sec)), (long)tv.tv_usec, (long)getpid()); log_info("Saving to %s/%s with uid %d", base_dir_name, problem_id, uid); struct dump_dir *dd; if (base_dir_name) dd = try_dd_create(base_dir_name, problem_id, uid); else { /* Try /var/run/abrt */ dd = try_dd_create(LOCALSTATEDIR"/run/abrt", problem_id, uid); /* Try $HOME/tmp */ if (!dd) { char *home = getenv("HOME"); if (home && home[0]) { home = concat_path_file(home, "tmp"); /*mkdir(home, 0777); - do we want this? */ dd = try_dd_create(home, problem_id, uid); free(home); } } //TODO: try user's home dir obtained by getpwuid(getuid())? /* Try system temporary directory */ if (!dd) dd = try_dd_create(LARGE_DATA_TMP_DIR, problem_id, uid); } if (!dd) /* try_dd_create() already emitted the error message */ goto ret; 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) { char *dest = concat_path_file(dd->dd_dirname, name); log_info("copying '%s' to '%s'", value->content, dest); off_t copied = copy_file(value->content, dest, DEFAULT_DUMP_DIR_MODE | S_IROTH); if (copied < 0) error_msg("Can't copy %s to %s", value->content, dest); else log_info("copied %li bytes", (unsigned long)copied); free(dest); continue; } /* only files should contain '/' and those are handled earlier */ if (name[0] == '.' || strchr(name, '/')) { error_msg("Problem data field name contains disallowed chars: '%s'", name); continue; } dd_save_text(dd, name, value->content); } /* need to create basic files AFTER we save the pd to dump_dir * otherwise we can't skip already created files like in case when * reporting from anaconda where we can't read /etc/{system,redhat}-release * and os_release is taken from anaconda */ dd_create_basic_files(dd, uid, NULL); problem_id[strlen(problem_id) - strlen(NEW_PD_SUFFIX)] = '\0'; char* new_path = concat_path_file(base_dir_name, problem_id); log_info("Renaming from '%s' to '%s'", dd->dd_dirname, new_path); dd_rename(dd, new_path); ret: free(problem_id); return dd; }
/* returns number of errors */ unsigned abrt_oops_create_dump_dirs(GList *oops_list, const char *dump_location, const char *analyzer, int flags) { const int oops_cnt = g_list_length(oops_list); unsigned countdown = ABRT_OOPS_MAX_DUMPED_COUNT; /* do not report hundreds of oopses */ log_notice("Saving %u oopses as problem dirs", oops_cnt >= countdown ? countdown : oops_cnt); char *cmdline_str = xmalloc_fopen_fgetline_fclose("/proc/cmdline"); char *fips_enabled = xmalloc_fopen_fgetline_fclose("/proc/sys/crypto/fips_enabled"); char *proc_modules = xmalloc_open_read_close("/proc/modules", /*maxsize:*/ NULL); char *suspend_stats = xmalloc_open_read_close("/sys/kernel/debug/suspend_stats", /*maxsize:*/ NULL); time_t t = time(NULL); const char *iso_date = iso_date_string(&t); /* dump should be readable by all if we're run with -x */ uid_t my_euid = (uid_t)-1L; mode_t mode = DEFAULT_DUMP_DIR_MODE | S_IROTH; /* and readable only for the owner otherwise */ if (!(flags & ABRT_OOPS_WORLD_READABLE)) { mode = DEFAULT_DUMP_DIR_MODE; my_euid = geteuid(); } pid_t my_pid = getpid(); unsigned idx = 0; unsigned errors = 0; while (idx < oops_cnt) { char base[sizeof("oops-YYYY-MM-DD-hh:mm:ss-%lu-%lu") + 2 * sizeof(long)*3]; sprintf(base, "oops-%s-%lu-%lu", iso_date, (long)my_pid, (long)idx); char *path = concat_path_file(dump_location, base); struct dump_dir *dd = dd_create(path, /*uid:*/ my_euid, mode); if (dd) { dd_create_basic_files(dd, /*uid:*/ my_euid, NULL); abrt_oops_save_data_in_dump_dir(dd, (char*)g_list_nth_data(oops_list, idx++), proc_modules); dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION); dd_save_text(dd, FILENAME_ANALYZER, "abrt-oops"); dd_save_text(dd, FILENAME_TYPE, "Kerneloops"); if (cmdline_str) dd_save_text(dd, FILENAME_CMDLINE, cmdline_str); if (proc_modules) dd_save_text(dd, "proc_modules", proc_modules); if (fips_enabled && strcmp(fips_enabled, "0") != 0) dd_save_text(dd, "fips_enabled", fips_enabled); if (suspend_stats) dd_save_text(dd, "suspend_stats", suspend_stats); dd_close(dd); notify_new_path(path); } else errors++; free(path); if (--countdown == 0) break; if (dd && (flags & ABRT_OOPS_THROTTLE_CREATION)) if (abrt_oops_signaled_sleep(1) > 0) break; } free(cmdline_str); free(proc_modules); free(fips_enabled); free(suspend_stats); return errors; }
void dump_docker_info(struct dump_dir *dd, const char *root_dir) { if (!dd_exist(dd, FILENAME_CONTAINER)) dd_save_text(dd, FILENAME_CONTAINER, "docker"); json_object *json = NULL; char *mntnf_path = concat_path_file(dd->dd_dirname, FILENAME_MOUNTINFO); FILE *mntnf_file = fopen(mntnf_path, "r"); free(mntnf_path); struct mount_point { const char *name; enum mountinfo_fields { MOUNTINFO_ROOT, MOUNTINFO_SOURCE, } field; } mount_points[] = { { "/sys/fs/cgroup/memory", MOUNTINFO_ROOT }, { "/", MOUNTINFO_SOURCE }, }; char *container_id = NULL; char *output = NULL; /* initialized to 0 because we call mountinfo_destroy below */ struct mountinfo mntnf = {0}; for (size_t i = 0; i < ARRAY_SIZE(mount_points); ++i) { log_debug("Parsing container ID from mount point '%s'", mount_points[i].name); rewind(mntnf_file); /* get_mountinfo_for_mount_point() re-initializes &mntnf */ mountinfo_destroy(&mntnf); int r = get_mountinfo_for_mount_point(mntnf_file, &mntnf, mount_points[i].name); if (r != 0) { log_debug("Mount poin not found"); continue; } const char *mnt_info = NULL; switch(mount_points[i].field) { case MOUNTINFO_ROOT: mnt_info = MOUNTINFO_ROOT(mntnf); break; case MOUNTINFO_SOURCE: mnt_info = MOUNTINFO_MOUNT_SOURCE(mntnf); break; default: error_msg("BUG: forgotten MOUNTINFO field type"); abort(); } const char *last = strrchr(mnt_info, '/'); if (last == NULL || strncmp("/docker-", last, strlen("/docker-")) != 0) { log_debug("Mounted source is not a docker mount source: '%s'", mnt_info); continue; } last = strrchr(last, '-'); if (last == NULL) { log_debug("The docker mount point has unknown format"); continue; } ++last; /* Why we copy only 12 bytes here? * Because only the first 12 characters are used by docker as ID of the * container. */ container_id = xstrndup(last, 12); if (strlen(container_id) != 12) { log_debug("Failed to get container ID"); continue; } char *docker_inspect_cmdline = NULL; if (root_dir != NULL) docker_inspect_cmdline = xasprintf("chroot %s /bin/sh -c \"docker inspect %s\"", root_dir, container_id); else docker_inspect_cmdline = xasprintf("docker inspect %s", container_id); log_debug("Executing: '%s'", docker_inspect_cmdline); output = run_in_shell_and_save_output(0, docker_inspect_cmdline, "/", NULL); free(docker_inspect_cmdline); if (output == NULL || strcmp(output, "[]\n") == 0) { log_debug("Unsupported container ID: '%s'", container_id); free(container_id); container_id = NULL; free(output); output = NULL; continue; } break; } fclose(mntnf_file); if (container_id == NULL) { error_msg("Could not inspect the container"); goto dump_docker_info_cleanup; } dd_save_text(dd, FILENAME_CONTAINER_ID, container_id); dd_save_text(dd, FILENAME_DOCKER_INSPECT, output); json = json_tokener_parse(output); free(output); if (is_error(json)) { error_msg("Unable parse response from docker"); goto dump_docker_info_cleanup; } json_object *container = json_object_array_get_idx(json, 0); if (container == NULL) { error_msg("docker does not contain array of containers"); goto dump_docker_info_cleanup; } json_object *config = NULL; if (!json_object_object_get_ex(container, "Config", &config)) { error_msg("container does not have 'Config' member"); goto dump_docker_info_cleanup; } json_object *image = NULL; if (!json_object_object_get_ex(config, "Image", &image)) { error_msg("Config does not have 'Image' member"); goto dump_docker_info_cleanup; } char *name = strtrimch(xstrdup(json_object_to_json_string(image)), '"'); dd_save_text(dd, FILENAME_CONTAINER_IMAGE, name); free(name); dump_docker_info_cleanup: if (json != NULL) json_object_put(json); mountinfo_destroy(&mntnf); return; }
int main(int argc, char **argv) { setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif abrt_init(argv); struct ureport_server_config config = { .ur_url = SERVER_URL, .ur_ssl_verify = true, }; bool insecure = !config.ur_ssl_verify; bool attach_reported_to = false; const char *dump_dir_path = "."; const char *ureport_hash = NULL; int rhbz_bug = -1; struct dump_dir *dd = NULL; struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT__DUMP_DIR(&dump_dir_path), OPT_STRING('u', "url", &config.ur_url, "URL", _("Specify server URL")), OPT_BOOL('k', "insecure", &insecure, _("Allow insecure connection to ureport server")), OPT_STRING('a', "attach", &ureport_hash, "BTHASH", _("bthash of uReport to attach")), OPT_INTEGER('b', "bug-id", &rhbz_bug, _("Attach RHBZ bug (requires -a)")), OPT_BOOL('r', "attach-reported-to", &attach_reported_to, _("Attach contents of reported_to")), OPT_END(), }; const char *program_usage_string = _( "& [-v] [-u URL] [-k] [-a bthash -b bug-id] [-r] [-d DIR]\n" "\n" "Upload micro report or add an attachment to a micro report" ); parse_opts(argc, argv, program_options, program_usage_string); config.ur_ssl_verify = !insecure; load_ureport_server_config(&config); post_state_t *post_state = NULL; /* we either need both -b & -a or none of them */ if (ureport_hash && rhbz_bug > 0) return perform_attach(&config, ureport_hash, rhbz_bug); if (ureport_hash && rhbz_bug <= 0) error_msg_and_die(_("You need to specify bug ID to attach.")); if (!ureport_hash && rhbz_bug > 0) error_msg_and_die(_("You need to specify bthash of the uReport to attach.")); /* -r */ if (attach_reported_to) { dd = dd_opendir(dump_dir_path, DD_OPEN_READONLY); if (!dd) xfunc_die(); report_result_t *ureport_result = find_in_reported_to(dd, "uReport"); report_result_t *bz_result = find_in_reported_to(dd, "Bugzilla"); dd_close(dd); if (!ureport_result || !ureport_result->bthash) error_msg_and_die(_("This problem does not have an uReport assigned.")); if (!bz_result || !bz_result->url) error_msg_and_die(_("This problem has not been reported to Bugzilla.")); char *bthash = xstrdup(ureport_result->bthash); free_report_result(ureport_result); char *bugid_ptr = strstr(bz_result->url, "show_bug.cgi?id="); if (!bugid_ptr) error_msg_and_die(_("Unable to find bug ID in bugzilla URL '%s'"), bz_result->url); bugid_ptr += strlen("show_bug.cgi?id="); int bugid; /* we're just reading int, sscanf works fine */ if (sscanf(bugid_ptr, "%d", &bugid) != 1) error_msg_and_die(_("Unable to parse bug ID from bugzilla URL '%s'"), bz_result->url); free_report_result(bz_result); const int result = perform_attach(&config, bthash, bugid); free(bthash); return result; } /* -b, -a nor -r were specified - upload uReport from dump_dir */ int ret = 1; /* "failure" (for now) */ char *dest_url = concat_path_file(config.ur_url, REPORT_URL_SFX); config.ur_url = dest_url; char *json_ureport = ureport_from_dump_dir(dump_dir_path); if (!json_ureport) { error_msg(_("Not uploading an empty uReport")); goto format_err; } post_state = post_ureport(json_ureport, &config); free(json_ureport); if (!post_state) { error_msg(_("Failed on submitting the problem")); goto format_err; } struct ureport_server_response *response = get_server_response(post_state, &config); if (!response) goto format_err; if (!response->is_error) { VERB1 log("is known: %s", response->value); ret = 0; /* "success" */ dd = dd_opendir(dump_dir_path, /* flags */ 0); if (!dd) xfunc_die(); if (response->bthash) { char *msg = xasprintf("uReport: BTHASH=%s", response->bthash); add_reported_to(dd, msg); free(msg); } if (response->reported_to_list) { for (GList *e = response->reported_to_list; e; e = g_list_next(e)) add_reported_to(dd, e->data); } if (response->solution) dd_save_text(dd, FILENAME_NOT_REPORTABLE, response->solution); dd_close(dd); /* If a reported problem is not known then emit NEEDMORE */ if (strcmp("true", response->value) == 0) { log(_("This problem has already been reported.")); if (response->message) log(response->message); ret = EXIT_STOP_EVENT_RUN; } } else { error_msg(_("Server responded with an error: '%s'"), response->value); } free_ureport_server_response(response); format_err: free_post_state(post_state); free(dest_url); return ret; }
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 of coredump in dump 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", _("Dump directory")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); /* Run unstrip -n and trim its output, leaving only sizes and build ids */ char *unstrip_n_output = run_unstrip_n(dump_dir_name, /*timeout_sec:*/ 30); if (!unstrip_n_output) return 1; /* bad dump_dir_name, can't run unstrip, etc... */ /* modifies unstrip_n_output in-place: */ trim_unstrip_output(unstrip_n_output, unstrip_n_output); /* Hash package + executable + unstrip_n_output and save it as UUID */ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return 1; char *executable = dd_load_text(dd, FILENAME_EXECUTABLE); char *package = dd_load_text(dd, FILENAME_PACKAGE); /* Package variable has "firefox-3.5.6-1.fc11[.1]" format */ /* Remove distro suffix and maybe least significant version number */ char *p = package; while (*p) { if (*p == '.' && (p[1] < '0' || p[1] > '9')) { /* We found "XXXX.nondigitXXXX", trim this part */ *p = '\0'; break; } p++; } char *first_dot = strchr(package, '.'); if (first_dot) { char *last_dot = strrchr(first_dot, '.'); if (last_dot != first_dot) { /* There are more than one dot: "1.2.3" * Strip last part, we don't want to distinquish crashes * in packages which differ only by minor release number. */ *last_dot = '\0'; } } char *string_to_hash = xasprintf("%s%s%s", package, executable, unstrip_n_output); /*free(package);*/ /*free(executable);*/ /*free(unstrip_n_output);*/ char hash_str[SHA1_RESULT_LEN*2 + 1]; create_hash(hash_str, string_to_hash); dd_save_text(dd, FILENAME_UUID, hash_str); dd_close(dd); return 0; }
static int SavePackageDescriptionToDebugDump(const char *dump_dir_name, const char *chroot) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return 1; char *type = dd_load_text(dd, FILENAME_TYPE); if (!strcmp(type, "Kerneloops")) { dd_save_text(dd, FILENAME_PACKAGE, "kernel"); dd_save_text(dd, FILENAME_COMPONENT, "kernel"); dd_close(dd); free(type); return 0; } free(type); char *cmdline = NULL; char *executable = NULL; char *rootdir = NULL; char *package_short_name = NULL; struct pkg_envra *pkg_name = NULL; char *component = NULL; int error = 1; /* note: "goto ret" statements below free all the above variables, * but they don't dd_close(dd) */ cmdline = dd_load_text_ext(dd, FILENAME_CMDLINE, DD_FAIL_QUIETLY_ENOENT); executable = dd_load_text(dd, FILENAME_EXECUTABLE); if (chroot == NULL) chroot = rootdir = dd_load_text_ext(dd, FILENAME_ROOTDIR, DD_FAIL_QUIETLY_ENOENT | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE); /* Close dd while we query package database. It can take some time, * don't want to keep dd locked longer than necessary */ dd_close(dd); if (is_path_blacklisted(executable)) { log("Blacklisted executable '%s'", executable); goto ret; /* return 1 (failure) */ } pkg_name = rpm_get_package_nvr(executable, chroot); if (!pkg_name) { if (settings_bProcessUnpackaged) { log_info("Crash in unpackaged executable '%s', " "proceeding without packaging information", executable); goto ret0; /* no error */ } log("Executable '%s' doesn't belong to any package" " and ProcessUnpackaged is set to 'no'", executable ); goto ret; /* return 1 (failure) */ } /* Check well-known interpreter names */ const char *basename = strrchr(executable, '/'); if (basename) basename++; else basename = executable; /* if basename is known interpreter, we want to blame the running script * not the interpreter */ if (g_list_find_custom(settings_Interpreters, basename, (GCompareFunc)g_strcmp0)) { struct pkg_envra *script_pkg = get_script_name(cmdline, &executable, chroot); /* executable may have changed, check it again */ if (is_path_blacklisted(executable)) { log("Blacklisted executable '%s'", executable); goto ret; /* return 1 (failure) */ } if (!script_pkg) { /* Script name is not absolute, or it doesn't * belong to any installed package. */ if (!settings_bProcessUnpackaged) { log("Interpreter crashed, but no packaged script detected: '%s'", cmdline); goto ret; /* return 1 (failure) */ } /* Unpackaged script, but the settings says we want to keep it. * BZ plugin wont allow to report this anyway, because component * is missing, so there is no reason to mark it as not_reportable. * Someone might want to use abrt to report it using ftp. */ goto ret0; } free_pkg_envra(pkg_name); pkg_name = script_pkg; } package_short_name = xasprintf("%s", pkg_name->p_name); log_info("Package:'%s' short:'%s'", pkg_name->p_nvr, package_short_name); if (g_list_find_custom(settings_setBlackListedPkgs, package_short_name, (GCompareFunc)g_strcmp0)) { log("Blacklisted package '%s'", package_short_name); goto ret; /* return 1 (failure) */ } if (settings_bOpenGPGCheck) { if (!rpm_chk_fingerprint(package_short_name)) { log("Package '%s' isn't signed with proper key", package_short_name); goto ret; /* return 1 (failure) */ } /* We used to also check the integrity of the executable here: * if (!CheckHash(package_short_name.c_str(), executable)) BOOM(); * Checking the MD5 sum requires to run prelink to "un-prelink" the * binaries - this is considered potential security risk so we don't * do it now, until we find some non-intrusive way. */ } component = rpm_get_component(executable, chroot); dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) goto ret; /* return 1 (failure) */ if (pkg_name) { dd_save_text(dd, FILENAME_PACKAGE, pkg_name->p_nvr); dd_save_text(dd, FILENAME_PKG_EPOCH, pkg_name->p_epoch); dd_save_text(dd, FILENAME_PKG_NAME, pkg_name->p_name); dd_save_text(dd, FILENAME_PKG_VERSION, pkg_name->p_version); dd_save_text(dd, FILENAME_PKG_RELEASE, pkg_name->p_release); dd_save_text(dd, FILENAME_PKG_ARCH, pkg_name->p_arch); } if (component) dd_save_text(dd, FILENAME_COMPONENT, component); dd_close(dd); ret0: error = 0; ret: free(cmdline); free(executable); free(rootdir); free(package_short_name); free_pkg_envra(pkg_name); free(component); return error; }
int main(int argc, char **argv) { /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif abrt_init(argv); char *i_opt = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [options] -d DIR\n" "\n" "Analyzes coredump in problem directory DIR, generates and saves backtrace" ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_i = 1 << 2, OPT_t = 1 << 3, }; /* 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( 'i', NULL, &i_opt , "DIR1[:DIR2]...", _("Additional debuginfo directories")), OPT_INTEGER('t', NULL, &exec_timeout_sec, _("Kill gdb if it runs for more than NUM seconds")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); map_string_t *settings = new_map_string(); if (!load_abrt_plugin_conf_file(CCPP_CONF, settings)) error_msg("Can't load '%s'", CCPP_CONF); const char *value = get_map_string_item_or_NULL(settings, "DebuginfoLocation"); char *debuginfo_location; if (value) debuginfo_location = xstrdup(value); else debuginfo_location = xstrdup(LOCALSTATEDIR"/cache/abrt-di"); free_map_string(settings); char *debuginfo_dirs = NULL; if (i_opt) debuginfo_dirs = xasprintf("%s:%s", debuginfo_location, i_opt); /* Create gdb backtrace */ char *backtrace = get_backtrace(dump_dir_name, exec_timeout_sec, (debuginfo_dirs) ? debuginfo_dirs : debuginfo_location); free(debuginfo_location); if (!backtrace) { backtrace = xstrdup(""); log_warning("get_backtrace() returns NULL, broken core/gdb?"); } free(debuginfo_dirs); free_abrt_conf_data(); /* Store gdb backtrace */ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return 1; dd_save_text(dd, FILENAME_BACKTRACE, backtrace); dd_close(dd); /* Don't be completely silent. gdb run takes a few seconds, * it is useful to let user know it (maybe) worked. */ log_warning(_("Backtrace is generated and saved, %u bytes"), (int)strlen(backtrace)); free(backtrace); return 0; }
mw_result_t LoadDebugDump(const char *dump_dir_name, problem_data_t **problem_data) { mw_result_t res; struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return MW_ERROR; struct cdump_state state; state.uid = dd_load_text(dd, FILENAME_UID); state.uuid = NULL; state.crash_dump_dup_name = NULL; char *analyzer = dd_load_text(dd, FILENAME_ANALYZER); dd_close(dd); res = MW_ERROR; /* Run post-create event handler(s) */ struct run_event_state *run_state = new_run_event_state(); run_state->post_run_callback = is_crash_a_dup; run_state->post_run_param = &state; run_state->logging_callback = do_log; int r = run_event_on_dir_name(run_state, dump_dir_name, "post-create"); free_run_event_state(run_state); //TODO: consider this case: // new dump is created, post-create detects that it is a dup, // but then FillCrashInfo(dup_name) *FAILS*. // In this case, we later delete damaged dup_name (right?) // but new dump never gets its FILENAME_COUNT set! /* Is crash a dup? (In this case, is_crash_a_dup() should have * aborted "post-create" event processing as soon as it saw uuid * and determined that there is another crash with same uuid. * In this case it sets state.crash_dump_dup_name) */ if (!state.crash_dump_dup_name) { /* No. Was there error on one of processing steps in run_event? */ if (r != 0) goto ret; /* yes */ /* Was uuid created after all? (In this case, is_crash_a_dup() * should have fetched it and created state.uuid) */ if (!state.uuid) { /* no */ log("Dump directory '%s' has no UUID element", dump_dir_name); goto ret; } } else { dump_dir_name = state.crash_dump_dup_name; } /* Loads problem_data (from the *first debugdump dir* if this one is a dup) * Returns: * MW_OCCURRED: "crash count is != 1" (iow: it is > 1 - dup) * MW_OK: "crash count is 1" (iow: this is a new crash, not a dup) * else: an error code */ { dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) { res = MW_ERROR; goto ret; } /* Reset mode/uig/gid to correct values for all files created by event run */ dd_sanitize_mode_and_owner(dd); /* Update count */ char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT); unsigned long count = strtoul(count_str, NULL, 10); count++; char new_count_str[sizeof(long)*3 + 2]; sprintf(new_count_str, "%lu", count); dd_save_text(dd, FILENAME_COUNT, new_count_str); dd_close(dd); *problem_data = FillCrashInfo(dump_dir_name); if (*problem_data != NULL) { res = MW_OK; if (count > 1) { log("Dump directory is a duplicate of %s", dump_dir_name); res = MW_OCCURRED; } } } ret: free(state.uuid); free(state.uid); free(state.crash_dump_dup_name); free(analyzer); return res; }
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; }
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; }
void abrt_oops_save_data_in_dump_dir(struct dump_dir *dd, char *oops, const char *proc_modules) { char *first_line = oops; char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */ *second_line++ = '\0'; if (first_line[0]) dd_save_text(dd, FILENAME_KERNEL, first_line); dd_save_text(dd, FILENAME_BACKTRACE, second_line); /* check if trace doesn't have line: 'Your BIOS is broken' */ if (strstr(second_line, "Your BIOS is broken")) dd_save_text(dd, FILENAME_NOT_REPORTABLE, _("A kernel problem occurred because of broken BIOS. " "Unfortunately, such problems are not fixable by kernel maintainers.")); /* check if trace doesn't have line: 'Your hardware is unsupported' */ else if (strstr(second_line, "Your hardware is unsupported")) dd_save_text(dd, FILENAME_NOT_REPORTABLE, _("A kernel problem occurred, but your hardware is unsupported, " "therefore kernel maintainers are unable to fix this problem.")); else { char *tainted_short = kernel_tainted_short(second_line); if (tainted_short) { log_notice("Kernel is tainted '%s'", tainted_short); dd_save_text(dd, FILENAME_TAINTED_SHORT, tainted_short); char *tnt_long = kernel_tainted_long(tainted_short); dd_save_text(dd, FILENAME_TAINTED_LONG, tnt_long); free(tnt_long); struct strbuf *reason = strbuf_new(); const char *fmt = _("A kernel problem occurred, but your kernel has been " "tainted (flags:%s). Kernel maintainers are unable to " "diagnose tainted reports."); strbuf_append_strf(reason, fmt, tainted_short); char *modlist = !proc_modules ? NULL : abrt_oops_list_of_tainted_modules(proc_modules); if (modlist) { strbuf_append_strf(reason, _(" Tainted modules: %s."), modlist); free(modlist); } dd_save_text(dd, FILENAME_NOT_REPORTABLE, reason->buf); strbuf_free(reason); free(tainted_short); } } // TODO: add "Kernel oops: " prefix, so that all oopses have recognizable FILENAME_REASON? // kernel oops 1st line may look quite puzzling otherwise... char *reason_pretty = NULL; char *error = NULL; struct sr_stacktrace *trace = sr_stacktrace_parse(SR_REPORT_KERNELOOPS, second_line, &error); if (trace) { reason_pretty = sr_stacktrace_get_reason(trace); sr_stacktrace_free(trace); } else free(error); if (reason_pretty) { dd_save_text(dd, FILENAME_REASON, reason_pretty); free(reason_pretty); } else dd_save_text(dd, FILENAME_REASON, second_line); }