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) { /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif abrt_init(argv); const char *program_usage_string = _( "& [-v -i -n INCREMENT] -e|--event EVENT DIR..." ); char *event_name = NULL; int interactive = 0; /* must be _int_, OPT_BOOL expects that! */ int nice_incr = 0; struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_STRING('e', "event" , &event_name, "EVENT", _("Run EVENT on DIR")), OPT_BOOL('i', "interactive" , &interactive, _("Communicate directly to the user")), OPT_INTEGER('n', "nice" , &nice_incr, _("Increment the nice value by INCREMENT")), OPT_END() }; parse_opts(argc, argv, program_options, program_usage_string); argv += optind; if (!*argv || !event_name) show_usage_and_die(program_usage_string, program_options); load_abrt_conf(); const char *const opt_env_nice = getenv("ABRT_EVENT_NICE"); if (opt_env_nice != NULL && opt_env_nice[0] != '\0') { log_debug("Using ABRT_EVENT_NICE=%s to increment the nice value", opt_env_nice); nice_incr = xatoi(opt_env_nice); } if (nice_incr != 0) { log_debug("Incrementing the nice value by %d", nice_incr); const int ret = nice(nice_incr); if (ret == -1) perror_msg_and_die("Failed to increment the nice value"); } bool post_create = (strcmp(event_name, "post-create") == 0); char *dump_dir_name = NULL; while (*argv) { dump_dir_name = xstrdup(*argv++); int i = strlen(dump_dir_name); while (--i >= 0) if (dump_dir_name[i] != '/') break; dump_dir_name[++i] = '\0'; struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ DD_OPEN_READONLY); if (!dd) return 1; uid = dd_load_text_ext(dd, FILENAME_UID, DD_FAIL_QUIETLY_ENOENT); dd_close(dd); struct run_event_state *run_state = new_run_event_state(); if (!interactive) make_run_event_state_forwarding(run_state); run_state->logging_callback = do_log; if (post_create) run_state->post_run_callback = is_crash_a_dup; int r = run_event_on_dir_name(run_state, dump_dir_name, event_name); const bool no_action_for_event = (r == 0 && run_state->children_count == 0); free_run_event_state(run_state); /* Needed only if is_crash_a_dup() was called, but harmless * even if it wasn't: */ dup_uuid_fini(); dup_corebt_fini(); if (no_action_for_event) error_msg_and_die("No actions are found for event '%s'", event_name); //TODO: consider this case: // new dump is created, post-create detects that it is a dup, // but then load_crash_info(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 crash_dump_dup_name) */ if (crash_dump_dup_name) error_msg_and_die("DUP_OF_DIR: %s", crash_dump_dup_name); /* Was there error on one of processing steps in run_event? */ if (r != 0) return r; /* yes */ free(dump_dir_name); dump_dir_name = NULL; } /* exit 0 means, that there is no duplicate of dump-dir */ return 0; }
int report_problem_in_dir(const char *dirname, int flags) { /* Prepare it before fork, to avoid thread-unsafe setenv there */ char *prgname = (char*) g_get_prgname(); if (prgname) prgname = xasprintf("LIBREPORT_PRGNAME=%s", prgname); fflush(NULL); pid_t pid = fork(); if (pid < 0) /* error */ { perror_msg("fork"); return -1; } if (pid == 0) /* child */ { const char *event_name; const char *path, *path1, *path2; char *args[7], **pp; /* Graphical tool */ event_name = "report-gui"; path1 = BIN_DIR"/report-gtk"; path2 = "report-gtk"; pp = args; *pp++ = (char *)"report-gtk"; if (flags & LIBREPORT_DEL_DIR) *pp++ = (char *)"--delete"; *pp++ = (char *)"--"; *pp++ = (char *)dirname; *pp = NULL; if (prgname) putenv(prgname); if (flags & LIBREPORT_RUN_NEWT) { /* we want to run newt first */ event_name = "report-cli"; path1 = BIN_DIR"/report-newt"; path2 = "report-newt"; pp = args; *pp++ = (char *)"report-newt"; if (flags & LIBREPORT_DEL_DIR) *pp++ = (char *)"--delete"; *pp++ = (char *)"--"; *pp++ = (char *)dirname; *pp = NULL; } else if (!getenv("DISPLAY") || (flags & LIBREPORT_RUN_CLI)) { /* GUI won't work, use command line tool instead */ event_name = "report-cli"; path1 = BIN_DIR"/report-cli"; path2 = "report-cli"; pp = args; *pp++ = (char *)"report-cli"; if (flags & LIBREPORT_DEL_DIR) *pp++ = (char *)"--delete"; *pp++ = (char *)"-e"; *pp++ = (char *)"report-cli"; *pp++ = (char *)"--"; *pp++ = (char *)dirname; *pp = NULL; } /* Some callers set SIGCHLD to SIG_IGN. * However, reporting spawns child processes. * Suppressing child death notification terribly confuses some of them. * Just in case, undo it. * Note that we do it in the child, so the parent is never affected. */ signal(SIGCHLD, SIG_DFL); if (!(flags & (LIBREPORT_WAIT | LIBREPORT_GETPID))) { /* Caller doesn't want to wait for completion (!LIBREPORT_WAIT), * and doesn't want to have pid returned (!LIBREPORT_GETPID). * Create a grandchild, and then exit. * This reparents grandchild to init, and makes waitpid * in parent detect our exit and return almost immediately. */ pid_t pid = fork(); if (pid < 0) /* error */ perror_msg_and_die("fork"); if (pid != 0) /* not grandchild */ { /* And now we exit: */ _exit(0); } /* There's an alternative approach to achieve this, * instead of using --delete. * We can create yet another intermediate process which * waits for reporting child to finish, and then * removes temporary dump dir. * Pros: deletion becomes more robust. * Even if child crashes, dir will be deleted. * Cons: having another process would use some resources, * and we'll need to at least close all open file descriptors, * and reopen stdio to /dev/null. We also might keep * a lot of libraries loaded: * who knows what parent process links against. * (can be worked around by exec'ing a "wait & delete" helper) */ } struct run_event_state *run_state = new_run_event_state(); int r = run_event_on_dir_name(run_state, dirname, event_name); int no_such_event = (r == 0 && run_state->children_count == 0); free_run_event_state(run_state); if (!no_such_event) { if (flags & LIBREPORT_DEL_DIR) { struct dump_dir *dd = dd_opendir(dirname, 0); if (dd) dd_delete(dd); } _exit(r); } /* No "report-cli/gui" event found, do it old-style */ path = path1; VERB1 log("Executing: %s", path); execv(path, args); /* Did not find the desired executable in the installation directory. * Trying to find it in PATH. */ path = path2; execvp(path, args); perror_msg_and_die("Can't execute %s", path); } /* parent */ free(prgname); if (!(flags & LIBREPORT_WAIT) && (flags & LIBREPORT_GETPID)) return pid; /* we are here either if LIBREPORT_WAIT (caller wants exitcode) * or !LIBREPORT_GETPID (caller doesn't want to have a child). * In both cases, we need to wait for child: */ int status; pid = safe_waitpid(pid, &status, 0); if (pid <= 0) { perror_msg("waitpid"); return -1; } if (WIFEXITED(status)) { VERB2 log("reporting finished with exitcode %d", WEXITSTATUS(status)); return WEXITSTATUS(status); } /* child died from a signal: WIFSIGNALED(status) should be true */ VERB2 log("reporting killed by signal %d", WTERMSIG(status)); return WTERMSIG(status) + 128; }