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); }
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; }