int main(int argc, char **argv) { abrt_init(argv); GList *dir_list = NULL; GList *file_list = NULL; char *preserve = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "\b [-v] [-d SIZE:DIR]... [-f SIZE:DIR]... [-p DIR]\n" "\n" "Deletes dump dirs (-d) or files (-f) in DIRs until they are smaller than SIZE" ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_f = 1 << 2, OPT_p = 1 << 3, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_LIST( 'd', NULL, &dir_list , "SIZE:DIR", _("Delete dump dirs")), OPT_LIST( 'f', NULL, &file_list, "SIZE:DIR", _("Delete files")), OPT_STRING('p', NULL, &preserve , "DIR" , _("Preserve this dump dir")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); argv += optind; if (argv[0] || !(dir_list || file_list)) show_usage_and_die(program_usage_string, program_options); export_abrt_envvars(0); g_list_foreach(dir_list, delete_dirs, preserve); g_list_foreach(file_list, delete_files, NULL); return 0; }
int main(int argc, char **argv) { abrt_init(argv); map_string_h *settings = new_map_string(); const char *dump_dir_name = "."; GList *conf_file = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "\b [-v] -c CONFFILE -d DIR\n" "\n" "Reports problem to Bugzilla" ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, }; /* 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_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); while (conf_file) { char *fn = (char *)conf_file->data; VERB1 log("Loading settings from '%s'", fn); load_conf_file(fn, settings, /*skip key w/o values:*/ true); VERB3 log("Loaded '%s'", fn); conf_file = g_list_remove(conf_file, fn); } VERB1 log("Initializing XML-RPC library"); xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_client_setup_global_const(&env); if (env.fault_occurred) error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code); xmlrpc_env_clean(&env); report_to_bugzilla(dump_dir_name, settings); free_map_string(settings); return 0; }
int main(int argc, char **argv) { /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif abrt_init(argv); GList *dir_list = NULL; GList *file_list = NULL; char *preserve = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [-v] [-d SIZE:DIR]... [-f SIZE:DIR]... [-p DIR] [FILE]...\n" "\n" "Deletes problem dirs (-d) or files (-f) in DIRs until they are smaller than SIZE.\n" "FILEs are preserved (never deleted)." ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_f = 1 << 2, OPT_p = 1 << 3, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_LIST('d' , NULL, &dir_list , "SIZE:DIR", _("Delete whole problem directories")), OPT_LIST('f' , NULL, &file_list, "SIZE:DIR", _("Delete files inside this directory")), OPT_STRING('p', NULL, &preserve, "DIR" , _("Preserve this directory")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); argv += optind; if ((argv[0] && !file_list) || !(dir_list || file_list) ) { show_usage_and_die(program_usage_string, program_options); } /* We don't have children, so this is not needed: */ //export_abrt_envvars(/*set_pfx:*/ 0); /* Preserve not only files specified on command line, but, * if they are symlinks, preserve also the real files they point to: */ GList *preserve_files_list = NULL; while (*argv) { char *name = *argv++; /* Since we don't bother freeing preserve_files_list on exit, * we take a shortcut and insert name instead of xstrdup(name) * in the next line: */ preserve_files_list = g_list_prepend(preserve_files_list, name); char *rp = realpath(name, NULL); if (rp) { if (strcmp(rp, name) != 0) preserve_files_list = g_list_prepend(preserve_files_list, rp); else free(rp); } } /* Not really necessary, but helps to reduce confusion when debugging */ preserve_files_list = g_list_reverse(preserve_files_list); g_list_foreach(dir_list, delete_dirs, preserve); g_list_foreach(file_list, delete_files, preserve_files_list); return 0; }
int main(int argc, char **argv) { /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif abrt_init(argv); page_size = sysconf(_SC_PAGE_SIZE); GList *match_list = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [-vs] [-F STR]... FILE PROG [ARGS]\n" "\n" "Watch log file FILE, run PROG when it grows or is replaced" ); enum { OPT_v = 1 << 0, OPT_s = 1 << 1, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_BOOL('s', NULL, NULL , _("Log to syslog")), OPT_LIST('F', NULL, &match_list, "STR", _("Don't run PROG if STRs aren't found")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); msg_prefix = g_progname; if ((opts & OPT_s) || getenv("ABRT_SYSLOG")) { logmode = LOGMODE_JOURNAL; } argv += optind; if (!argv[0] || !argv[1]) show_usage_and_die(program_usage_string, program_options); /* We want to support -F "`echo foo; echo bar`" - * need to split strings by newline, and be careful about * possible last empty string: "foo\nbar\n" = "foo", "bar", * NOT "foo", "bar", ""! */ for (GList *l = match_list; l; l = l->next) { char *eol = strchr((char*)l->data, '\n'); if (!eol) continue; *eol++ = '\0'; if (!*eol) continue; l = g_list_append(l, eol); /* in fact, always returns unchanged l */ } const char *filename = *argv++; int inotify_fd = inotify_init(); if (inotify_fd == -1) perror_msg_and_die("inotify_init failed"); close_on_exec_on(inotify_fd); struct stat statbuf; int file_fd = -1; int wd = -1; while (1) { /* If file is already opened, scan it from current pos */ if (file_fd >= 0) { memset(&statbuf, 0, sizeof(statbuf)); if (fstat(file_fd, &statbuf) != 0) goto close_fd; run_scanner_prog(file_fd, &statbuf, match_list, argv); /* Was file deleted or replaced? */ ino_t fd_ino = statbuf.st_ino; if (stat(filename, &statbuf) != 0 || statbuf.st_ino != fd_ino) /* yes */ { log_info("Inode# changed, closing fd"); close_fd: close(file_fd); if (wd >= 0) inotify_rm_watch(inotify_fd, wd); file_fd = -1; wd = -1; } } /* If file isn't opened, try to open it and scan */ if (file_fd < 0) { file_fd = open(filename, O_RDONLY); if (file_fd >= 0) { log_info("Opened '%s'", filename); /* For -w case, if we don't have inotify watch yet, open one */ if (wd < 0) { wd = inotify_add_watch(inotify_fd, filename, IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF); if (wd < 0) perror_msg("inotify_add_watch failed on '%s'", filename); else log_info("Added inotify watch for '%s'", filename); } if (fstat(file_fd, &statbuf) == 0) { /* If file is large, skip the beginning. * IOW: ignore old log messages because they are unlikely * to have sufficiently recent data to be useful. */ if (statbuf.st_size > (MAX_SCAN_BLOCK - READ_AHEAD)) lseek(file_fd, statbuf.st_size - (MAX_SCAN_BLOCK - READ_AHEAD), SEEK_SET); /* Note that statbuf is filled by fstat by now, * run_scanner_prog needs that */ run_scanner_prog(file_fd, &statbuf, match_list, argv); } } } /* Even if log file grows all the time, say, a new line every 5 ms, * we don't want to scan it all the time. Sleep a bit and let it grow * in bigger increments. * Sleep longer if file does not exist. */ sleep(file_fd >= 0 ? 1 : 59); /* Now wait for it to change, be moved or deleted */ if (wd >= 0) { char buf[4096]; log_debug("Waiting for '%s' to change", filename); /* We block here: */ int len = read(inotify_fd, buf, sizeof(buf)); if (len < 0 && errno != EINTR) /* I saw EINTR here on strace attach */ perror_msg("Error reading inotify fd"); /* we don't actually check what happened to file - * the code will handle all possibilities. */ log_debug("Change in '%s' detected", filename); /* Let them finish writing to the log file. otherwise * we may end up trying to analyze partial oops. */ sleep(1); } } /* while (1) */ return 0; }
int main(int argc, char **argv) { abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "\n& [-vbf] [-g GROUP-NAME]... [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR" "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..." "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w" "\nor:" "\n& [-v] [-c CONFFILE]... -h DUPHASH" "\n" "\nReports problem to Bugzilla." "\n" "\nThe tool reads DIR. Then it logs in to Bugzilla and tries to find a bug" "\nwith the same abrt_hash:HEXSTRING in 'Whiteboard'." "\n" "\nIf such bug is not found, then a new bug is created. Elements of DIR" "\nare stored in the bug as part of bug description or as attachments," "\ndepending on their type and size." "\n" "\nOtherwise, if such bug is found and it is marked as CLOSED DUPLICATE," "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE bug." "\nThe tool adds a new comment to found bug." "\n" "\nThe URL to new or modified bug is printed to stdout and recorded in" "\n'reported_to' element." "\n" "\nOption -t uploads FILEs to the already created bug on Bugzilla site." "\nThe bug ID is retrieved from directory specified by -d DIR." "\nIf problem data in DIR was never reported to Bugzilla, upload will fail." "\n" "\nOption -tID uploads FILEs to the bug with specified ID on Bugzilla site." "\n-d DIR is ignored." "\n" "\nOption -w adds bugzilla user to bug's CC list." "\n" "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/bugzilla.conf" "\nIts lines should have 'PARAM = VALUE' format." "\nRecognized string parameters: BugzillaURL, Login, Password, OSRelease." "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify." "\nParameters can be overridden via $Bugzilla_PARAM environment variables." "\n" "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/bugzilla_format.conf" ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, OPT_F = 1 << 3, OPT_A = 1 << 4, OPT_t = 1 << 5, OPT_b = 1 << 6, OPT_f = 1 << 7, OPT_w = 1 << 8, OPT_h = 1 << 9, OPT_g = 1 << 10, OPT_D = 1 << 11, }; const char *dump_dir_name = "."; GList *conf_file = NULL; const char *fmt_file = CONF_DIR"/plugins/bugzilla_format.conf"; const char *fmt_file2 = fmt_file; char *abrt_hash = NULL; char *ticket_no = NULL; char *debug_str = NULL; GList *group = NULL; /* 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_LIST( 'c', NULL, &conf_file , "FILE" , _("Configuration file (may be given many times)")), OPT_STRING( 'F', NULL, &fmt_file , "FILE" , _("Formatting file for initial comment")), OPT_STRING( 'A', NULL, &fmt_file2 , "FILE" , _("Formatting file for duplicates")), OPT_OPTSTRING('t', "ticket", &ticket_no , "ID" , _("Attach FILEs [to bug with this ID]")), OPT_BOOL( 'b', NULL, NULL, _("When creating bug, attach binary files too")), OPT_BOOL( 'f', NULL, NULL, _("Force reporting even if this problem is already reported")), OPT_BOOL( 'w', NULL, NULL, _("Add bugzilla user to CC list [of bug with this ID]")), OPT_STRING( 'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")), OPT_LIST( 'g', "group", &group , "GROUP" , _("Restrict access to this group only")), OPT_OPTSTRING('D', "debug", &debug_str , "STR" , _("Debug")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); argv += optind; export_abrt_envvars(0); map_string_t *settings = new_map_string(); struct bugzilla_struct rhbz = { 0 }; { if (!conf_file) conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/bugzilla.conf"); while (conf_file) { char *fn = (char *)conf_file->data; VERB1 log("Loading settings from '%s'", fn); load_conf_file(fn, settings, /*skip key w/o values:*/ false); VERB3 log("Loaded '%s'", fn); conf_file = g_list_delete_link(conf_file, conf_file); } set_settings(&rhbz, settings); /* WRONG! set_settings() does not copy the strings, it merely sets up pointers * to settings[] dictionary: */ /*free_map_string(settings);*/ } VERB1 log("Initializing XML-RPC library"); xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_client_setup_global_const(&env); if (env.fault_occurred) abrt_xmlrpc_die(&env); xmlrpc_env_clean(&env); struct abrt_xmlrpc *client; client = abrt_xmlrpc_new_client(rhbz.b_bugzilla_xmlrpc, rhbz.b_ssl_verify); unsigned rhbz_ver = rhbz_version(client); if (abrt_hash) { log(_("Looking for similar problems in bugzilla")); char *hash; if (prefixcmp(abrt_hash, "abrt_hash:")) hash = xasprintf("abrt_hash:%s", abrt_hash); else hash = xstrdup(abrt_hash); /* it's Fedora specific */ xmlrpc_value *all_bugs = rhbz_search_duphash(client, /*product:*/ "Fedora", /*version:*/ NULL, /*component:*/ NULL, hash); free(hash); unsigned all_bugs_size = rhbz_array_size(all_bugs); if (all_bugs_size > 0) { int bug_id = rhbz_get_bug_id_from_array0(all_bugs, rhbz_ver); printf("%i\n", bug_id); } return EXIT_SUCCESS; } if (rhbz.b_login[0] == '\0') { free(rhbz.b_login); rhbz.b_login = ask_bz_login(_("Login is not provided by configuration. Please enter your BZ login:"******"Password is not provided by configuration. Please enter the password for '%s':"), rhbz.b_login); rhbz.b_password = ask_bz_password(question); free(question); } if (opts & OPT_t) { if ((!argv[0] && !(opts & OPT_w)) || (argv[0] && (opts & OPT_w))) show_usage_and_die(program_usage_string, program_options); if (!ticket_no) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "Bugzilla:"); dd_close(dd); if (!reported_to || !reported_to->url) error_msg_and_die(_("Can't get Bugzilla ID because this problem has not yet been reported to Bugzilla.")); char *url = reported_to->url; reported_to->url = NULL; free_report_result(reported_to); if (prefixcmp(url, rhbz.b_bugzilla_url) != 0) error_msg_and_die(_("This problem has been reported to Bugzilla '%s' which differs from the configured Bugzilla '%s'."), url, rhbz.b_bugzilla_url); ticket_no = strrchr(url, '='); if (!ticket_no) error_msg_and_die(_("Malformed url to Bugzilla '%s'."), url); /* won't ever call free on it - it simplifies the code a lot */ ticket_no = xstrdup(ticket_no + 1); log(_("Using Bugzilla ID '%s'"), ticket_no); } login(client, &rhbz); if (opts & OPT_w) rhbz_mail_to_cc(client, xatoi_positive(ticket_no), rhbz.b_login, /* require mail notify */ 0); else { /* Attach files to existing BZ */ while (*argv) { const char *filename = *argv++; VERB1 log("Attaching file '%s' to bug %s", filename, ticket_no); int fd = open(filename, O_RDONLY); if (fd < 0) { perror_msg("Can't open '%s'", filename); continue; } struct stat st; if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) { error_msg("'%s': not a regular file", filename); close(fd); continue; } rhbz_attach_fd(client, ticket_no, filename, fd, /*flags*/ 0); close(fd); } } log(_("Logging out")); rhbz_logout(client); #if 0 /* enable if you search for leaks (valgrind etc) */ abrt_xmlrpc_free_client(client); #endif return 0; } /* Create new bug in Bugzilla */ if (!(opts & OPT_f)) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "Bugzilla:"); dd_close(dd); if (reported_to && reported_to->url) { char *msg = xasprintf("This problem was already reported to Bugzilla (see '%s')." " Do you still want to create a new bug?", reported_to->url); int yes = ask_yes_no(msg); free(msg); if (!yes) return 0; } free_report_result(reported_to); } problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name); if (!problem_data) xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */ const char *component = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT); const char *duphash = problem_data_get_content_or_NULL(problem_data, FILENAME_DUPHASH); //COMPAT, remove after 2.1 release if (!duphash) duphash = problem_data_get_content_or_die(problem_data, "global_uuid"); if (!rhbz.b_product || !*rhbz.b_product) /* if not overridden or empty... */ { free(rhbz.b_product); free(rhbz.b_product_version); map_string_t *osinfo = new_map_string(); problem_data_get_osinfo(problem_data, osinfo); parse_osinfo_for_bz(osinfo, &rhbz.b_product, &rhbz.b_product_version); free_map_string(osinfo); if (!rhbz.b_product) error_msg_and_die(_("Can't determine Bugzilla Product from problem data.")); } if (opts & OPT_D) { GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file); struct strbuf *bzcomment_buf = strbuf_new(); generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); char *bzcomment = strbuf_free_nobuf(bzcomment_buf); char *summary = create_summary_string(problem_data, comment_fmt_spec); printf("summary: %s\n" "\n" "%s" , summary, bzcomment ); free(bzcomment); free(summary); exit(0); } login(client, &rhbz); int bug_id = 0; /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345 * and won't search on bz server. */ char *remote_result; remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT); if (remote_result) { char *cmd = strtok(remote_result, " \n"); char *id = strtok(NULL, " \n"); if (!prefixcmp(cmd, "DUPLICATE")) { errno = 0; char *e; bug_id = strtoul(id, &e, 10); if (errno || id == e || *e != '\0' || bug_id > INT_MAX) { /* error / no digits / illegal trailing chars / too big a number */ bug_id = 0; } } } struct bug_info *bz = NULL; if (!bug_id) { log(_("Checking for duplicates")); int existing_id = -1; int crossver_id = -1; { /* Figure out whether we want to match component * when doing dup search. */ const char *component_substitute = is_in_comma_separated_list(component, rhbz.b_DontMatchComponents) ? NULL : component; /* We don't do dup detection across versions (see below why), * but we do add a note if cross-version potential dup exists. * For that, we search for cross version dups first: */ xmlrpc_value *crossver_bugs = rhbz_search_duphash(client, rhbz.b_product, /*version:*/ NULL, component_substitute, duphash); unsigned crossver_bugs_count = rhbz_array_size(crossver_bugs); VERB3 log("Bugzilla has %i reports with duphash '%s' including cross-version ones", crossver_bugs_count, duphash); if (crossver_bugs_count > 0) crossver_id = rhbz_get_bug_id_from_array0(crossver_bugs, rhbz_ver); xmlrpc_DECREF(crossver_bugs); if (crossver_bugs_count > 0) { /* In dup detection we require match in product *and version*. * Otherwise we sometimes have bugs in e.g. Fedora 17 * considered to be dups of Fedora 16 bugs. * Imagine that F16 is "end-of-lifed" - allowing cross-version * match will make all newly detected crashes DUPed * to a bug in a dead release. */ xmlrpc_value *dup_bugs = rhbz_search_duphash(client, rhbz.b_product, rhbz.b_product_version, component_substitute, duphash); unsigned dup_bugs_count = rhbz_array_size(dup_bugs); VERB3 log("Bugzilla has %i reports with duphash '%s'", dup_bugs_count, duphash); if (dup_bugs_count > 0) existing_id = rhbz_get_bug_id_from_array0(dup_bugs, rhbz_ver); xmlrpc_DECREF(dup_bugs); } } if (existing_id < 0) { /* Create new bug */ log(_("Creating a new bug")); GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file); struct strbuf *bzcomment_buf = strbuf_new(); generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); if (crossver_id >= 0) strbuf_append_strf(bzcomment_buf, "\nPotential duplicate: bug %u\n", crossver_id); char *bzcomment = strbuf_free_nobuf(bzcomment_buf); char *summary = create_summary_string(problem_data, comment_fmt_spec); int new_id = rhbz_new_bug(client, problem_data, rhbz.b_product, rhbz.b_product_version, summary, bzcomment, group ); free(bzcomment); free(summary); if (new_id == -1) { error_msg_and_die(_("Failed to create a new bug.")); } log(_("Adding attachments to bug %i"), new_id); char new_id_str[sizeof(int)*3 + 2]; sprintf(new_id_str, "%i", new_id); attach_files(client, new_id_str, problem_data, comment_fmt_spec); //TODO: free_comment_fmt_spec(comment_fmt_spec); bz = new_bug_info(); bz->bi_status = xstrdup("NEW"); bz->bi_id = new_id; goto log_out; } bug_id = existing_id; } bz = rhbz_bug_info(client, bug_id); log(_("Bug is already reported: %i"), bz->bi_id); /* Follow duplicates */ 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) { /* Add user's login to CC if not there already */ if (strcmp(bz->bi_reporter, rhbz.b_login) != 0 && !g_list_find_custom(bz->bi_cc_list, rhbz.b_login, (GCompareFunc)g_strcmp0) ) { log(_("Adding %s to CC list"), rhbz.b_login); rhbz_mail_to_cc(client, bz->bi_id, rhbz.b_login, RHBZ_NOMAIL_NOTIFY); } /* Add comment and bt */ const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT); if (comment && comment[0]) { GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file2); struct strbuf *bzcomment_buf = strbuf_new(); generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); char *bzcomment = strbuf_free_nobuf(bzcomment_buf); //TODO: free_comment_fmt_spec(comment_fmt_spec); int dup_comment = is_comment_dup(bz->bi_comments, bzcomment); if (!dup_comment) { log(_("Adding new comment to bug %d"), bz->bi_id); rhbz_add_comment(client, bz->bi_id, bzcomment, 0); free(bzcomment); const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); unsigned rating = 0; const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING); /* python doesn't have rating file */ if (rating_str) rating = xatou(rating_str); if (bt && rating > bz->bi_best_bt_rating) { char bug_id_str[sizeof(int)*3 + 2]; sprintf(bug_id_str, "%i", bz->bi_id); log(_("Attaching better backtrace")); rhbz_attach_blob(client, bug_id_str, FILENAME_BACKTRACE, bt, strlen(bt), RHBZ_NOMAIL_NOTIFY); } } else { free(bzcomment); log(_("Found the same comment in the bug history, not adding a new one")); } } } log_out: 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 : "", rhbz.b_bugzilla_url, bz->bi_id); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (dd) { char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", rhbz.b_bugzilla_url, bz->bi_id); add_reported_to(dd, msg); free(msg); dd_close(dd); } #if 0 /* enable if you search for leaks (valgrind etc) */ free(rhbz.b_product); free(rhbz.b_product_version); problem_data_free(problem_data); free_bug_info(bz); abrt_xmlrpc_free_client(client); #endif return 0; }
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; ureport_server_config_init(&config); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_u = 1 << 2, OPT_k = 1 << 3, OPT_t = 1 << 4, OPT_h = 1 << 5, OPT_i = 1 << 6, }; int ret = 1; /* "failure" (for now) */ int insecure = !config.ur_ssl_verify; const char *conf_file = UREPORT_CONF_FILE_PATH; const char *arg_server_url = NULL; const char *client_auth = NULL; const char *http_auth = NULL; GList *auth_items = NULL; const char *dump_dir_path = "."; const char *ureport_hash = NULL; int ureport_hash_from_rt = 0; int rhbz_bug = -1; int rhbz_bug_from_rt = 0; const char *email_address = NULL; int email_address_from_env = 0; char *comment = NULL; int comment_file = 0; char *attach_value = NULL; char *attach_value_from_rt = NULL; char *attach_value_from_rt_data = NULL; char *report_result_type = NULL; char *attach_type = NULL; struct dump_dir *dd = NULL; struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT__DUMP_DIR(&dump_dir_path), OPT_STRING('u', "url", &arg_server_url, "URL", _("Specify server URL")), OPT_BOOL('k', "insecure", &insecure, _("Allow insecure connection to ureport server")), OPT_STRING('t', "auth", &client_auth, "SOURCE", _("Use client authentication")), OPT_STRING('h', "http-auth", &http_auth, "CREDENTIALS", _("Use HTTP Authentication")), OPT_LIST('i', "auth_items", &auth_items, "AUTH_ITEMS", _("Additional files included in 'auth' key")), OPT_STRING('c', NULL, &conf_file, "FILE", _("Configuration file")), OPT_STRING('a', "attach", &ureport_hash, "BTHASH", _("bthash of uReport to attach (conflicts with -A)")), OPT_BOOL('A', "attach-rt", &ureport_hash_from_rt, _("attach to a bthash from reported_to (conflicts with -a)")), OPT_STRING('e', "email", &email_address, "EMAIL", _("contact e-mail address (requires -a|-A, conflicts with -E)")), OPT_BOOL('E', "email-env", &email_address_from_env, _("contact e-mail address from environment or configuration file (requires -a|-A, conflicts with -e)")), OPT_INTEGER('b', "bug-id", &rhbz_bug, _("attach RHBZ bug (requires -a|-A, conflicts with -B)")), OPT_BOOL('B', "bug-id-rt", &rhbz_bug_from_rt, _("attach last RHBZ bug from reported_to (requires -a|-A, conflicts with -b)")), OPT_STRING('o', "comment", &comment, "DESCRIPTION", _("attach short text (requires -a|-A, conflicts with -D)")), OPT_BOOL('O', "comment-file", &comment_file, _("attach short text from comment (requires -a|-A, conflicts with -d)")), /* va l ue */ OPT_STRING('l', "value", &attach_value, "DATA", _("attach value (requires -a|-A and -T, conflicts with -L)")), OPT_STRING('L', "value-rt", &attach_value_from_rt, "FIELD", _("attach data of FIELD [URL] of the last report result (requires -a|-A, -r and -T, conflicts with -l)")), OPT_STRING('r', "report-result-type", &report_result_type, "REPORT_RESULT_TYPE", _("use REPORT_RESULT_TYPE when looking for FIELD in reported_to (used only with -L)")), OPT_STRING('T', "type", &attach_type, "ATTACHMENT_TYPE", _("attach DATA as ureport attachment ATTACHMENT_TYPE (used only with -l|-L)")), OPT_END(), }; const char *program_usage_string = _( "& [-v] [-c FILE] [-u URL] [-k] [-t SOURCE] [-h CREDENTIALS]\n" " [-A -a bthash -B -b bug-id -E -e email -O -o comment] [-d DIR]\n" " [-A -a bthash -T ATTACHMENT_TYPE -r REPORT_RESULT_TYPE -L RESULT_FIELD] [-d DIR]\n" " [-A -a bthash -T ATTACHMENT_TYPE -l DATA] [-d DIR]\n" "& [-v] [-c FILE] [-u URL] [-k] [-t SOURCE] [-h CREDENTIALS] [-i AUTH_ITEMS] [-d DIR]\n" "\n" "Upload micro report or add an attachment to a micro report\n" "\n" "Reads the default configuration from "UREPORT_CONF_FILE_PATH ); unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); map_string_t *settings = new_map_string(); load_conf_file(conf_file, settings, /*skip key w/o values:*/ false); ureport_server_config_load(&config, settings); if (opts & OPT_u) ureport_server_config_set_url(&config, xstrdup(arg_server_url)); if (opts & OPT_k) config.ur_ssl_verify = !insecure; if (opts & OPT_t) ureport_server_config_set_client_auth(&config, client_auth); if (opts & OPT_h) ureport_server_config_load_basic_auth(&config, http_auth); if (opts & OPT_i) { g_list_free_full(config.ur_prefs.urp_auth_items, free); config.ur_prefs.urp_auth_items = auth_items; } if (!config.ur_url) ureport_server_config_set_url(&config, xstrdup(DEFAULT_WEB_SERVICE_URL)); if (ureport_hash && ureport_hash_from_rt) error_msg_and_die("You need to pass either -a bthash or -A"); if (rhbz_bug >= 0 && rhbz_bug_from_rt) error_msg_and_die("You need to pass either -b bug-id or -B"); if (email_address && email_address_from_env) error_msg_and_die("You need to pass either -e bthash or -E"); if (comment && comment_file) error_msg_and_die("You need to pass either -o comment or -O"); if (attach_value && attach_value_from_rt) error_msg_and_die("You need to pass either -l url or -L"); if ((attach_value || attach_value_from_rt) && attach_type == NULL) error_msg_and_die("You need to pass -T together with -l and -L"); if (attach_value_from_rt) { if (report_result_type == NULL) error_msg_and_die("You need to pass -r together with -L"); /* If you introduce a new recognized value, don't forget to update * the documentation and the conditions below. */ if (strcmp(attach_value_from_rt, "URL") != 0) error_msg_and_die("-L accepts only 'URL'"); } if (ureport_hash_from_rt || rhbz_bug_from_rt || comment_file || attach_value_from_rt) { dd = dd_opendir(dump_dir_path, DD_OPEN_READONLY); if (!dd) xfunc_die(); if (ureport_hash_from_rt) { report_result_t *ureport_result = find_in_reported_to(dd, "uReport"); if (!ureport_result || !ureport_result->bthash) error_msg_and_die(_("This problem does not have an uReport assigned.")); /* sorry, this will be leaked */ ureport_hash = xstrdup(ureport_result->bthash); free_report_result(ureport_result); } if (rhbz_bug_from_rt) { report_result_t *bz_result = find_in_reported_to(dd, "Bugzilla"); if (!bz_result || !bz_result->url) error_msg_and_die(_("This problem has not been reported to Bugzilla.")); 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="); /* we're just reading int, sscanf works fine */ if (sscanf(bugid_ptr, "%d", &rhbz_bug) != 1) error_msg_and_die(_("Unable to parse bug ID from bugzilla URL '%s'"), bz_result->url); free_report_result(bz_result); } if (comment_file) { comment = dd_load_text(dd, FILENAME_COMMENT); if (comment == NULL) error_msg_and_die(_("Cannot attach comment from 'comment' file")); if (comment[0] == '\0') error_msg_and_die(_("'comment' file is empty")); } if (attach_value_from_rt) { report_result_t *result = find_in_reported_to(dd, report_result_type); if (!result) error_msg_and_die(_("This problem has not been reported to '%s'."), report_result_type); /* If you introduce a new attach_value_from_rt recognized value, * this condition will become invalid. */ if (!result->url) error_msg_and_die(_("The report result '%s' is missing URL."), report_result_type); /* Avoid the need to duplicate the string. */ attach_value = attach_value_from_rt_data = result->url; result->url = NULL; free_report_result(result); } dd_close(dd); } if (email_address_from_env) { UREPORT_OPTION_VALUE_FROM_CONF(settings, "ContactEmail", email_address, (const char *)); if (!email_address) error_msg_and_die(_("Neither environment variable 'uReport_ContactEmail' nor configuration option 'ContactEmail' is set")); } if (ureport_hash) { if (rhbz_bug < 0 && !email_address && !comment && !attach_value) error_msg_and_die(_("You need to specify bug ID, contact email, comment or all of them")); if (rhbz_bug >= 0) { if (ureport_attach_int(ureport_hash, "RHBZ", rhbz_bug, &config)) goto finalize; } if (email_address) { if (ureport_attach_string(ureport_hash, "email", email_address, &config)) goto finalize; } if (comment) { if (ureport_attach_string(ureport_hash, "comment", comment, &config)) goto finalize; } if (attach_value) { if (ureport_attach_string(ureport_hash, attach_type, attach_value, &config)) goto finalize; } ret = 0; goto finalize; } if (!ureport_hash && (rhbz_bug >= 0 || email_address)) error_msg_and_die(_("You need to specify bthash of the uReport to attach.")); struct ureport_preferences *prefs = &(config.ur_prefs); prefs->urp_flags |= UREPORT_PREF_FLAG_RETURN_ON_FAILURE; char *json_ureport = ureport_from_dump_dir_ext(dump_dir_path, prefs); if (!json_ureport) { error_msg(_("Failed to generate microreport from the problem data")); goto finalize; } struct ureport_server_response *response = ureport_submit(json_ureport, &config); free(json_ureport); if (!response) goto finalize; if (!response->urr_is_error) { log_notice("is known: %s", response->urr_value); ret = 0; /* "success" */ if (!ureport_server_response_save_in_dump_dir(response, dump_dir_path, &config)) xfunc_die(); /* If a reported problem is not known then emit NEEDMORE */ if (strcmp("true", response->urr_value) == 0) { log(_("This problem has already been reported.")); if (response->urr_message) log("%s", response->urr_message); ret = EXIT_STOP_EVENT_RUN; } } else error_msg(_("Server responded with an error: '%s'"), response->urr_value); ureport_server_response_free(response); finalize: free(attach_value_from_rt_data); if (config.ur_prefs.urp_auth_items == auth_items) config.ur_prefs.urp_auth_items = NULL; free_map_string(settings); ureport_server_config_destroy(&config); return ret; }
int main(int argc, char **argv) { int expert_mode = 0; const char *prgname = "abrt"; abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif /* without this the name is set to argv[0] which confuses * desktops which uses the name to find the corresponding .desktop file * trac#180 * * env variable can be used to override the default prgname, so it's the * same as the application which is calling us (trac#303) * * note that g_set_prgname has to be called before gtk_init */ char *env_prgname = getenv("LIBREPORT_PRGNAME"); g_set_prgname(env_prgname ? env_prgname : prgname); gtk_init(&argc, &argv); /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [-vpdx] [-e EVENT]... [-g GUI_FILE] PROBLEM_DIR\n" "\n" "GUI tool to analyze and report problem saved in specified PROBLEM_DIR" ); enum { OPT_v = 1 << 0, OPT_g = 1 << 1, OPT_p = 1 << 2, OPT_d = 1 << 3, OPT_e = 1 << 4, OPT_x = 1 << 5, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_STRING('g', NULL, &g_glade_file, "FILE", _("Alternate GUI file")), OPT_BOOL( 'p', NULL, NULL, _("Add program names to log")), OPT_BOOL( 'd', "delete", NULL, _("Remove PROBLEM_DIR after reporting")), OPT_LIST( 'e', "event", &g_auto_event_list, "EVENT", _("Run only these events")), OPT_BOOL( 'x', "expert", &expert_mode, _("Expert mode")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); argv += optind; if (!argv[0] || argv[1]) /* zero or >1 arguments */ show_usage_and_die(program_usage_string, program_options); /* Allow algorithms to add mallocated strings */ for (GList *elem = g_auto_event_list; elem; elem = g_list_next(elem)) elem->data = xstrdup((const char *)elem->data); export_abrt_envvars(opts & OPT_p); g_dump_dir_name = xstrdup(argv[0]); /* load /etc/abrt/events/foo.{conf,xml} stuff and $XDG_CACHE_HOME/abrt/events/foo.conf */ g_event_config_list = load_event_config_data(); load_event_config_data_from_user_storage(g_event_config_list); load_user_settings("report-gtk"); load_workflow_config_data(WORKFLOWS_DIR); /* list of workflows applicable to the currently processed problem */ GList *possible_names = list_possible_events_glist(g_dump_dir_name, "workflow"); GHashTable *possible_workflows = load_workflow_config_data_from_list(possible_names, WORKFLOWS_DIR); g_list_free_full(possible_names, free); /* if we have only 1 workflow, we can use the events from it as default */ if (!expert_mode && g_auto_event_list == NULL && g_hash_table_size(possible_workflows) == 1) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, possible_workflows); if (g_hash_table_iter_next(&iter, &key, &value)) { log_notice("autoselected workflow: '%s'", (char *)key); g_auto_event_list = wf_get_event_names((workflow_t *)value); } } g_hash_table_destroy(possible_workflows); problem_data_reload_from_dump_dir(); g_custom_logger = &show_error_as_msgbox; GtkApplication *app = gtk_application_new("org.freedesktop.libreport.report", G_APPLICATION_NON_UNIQUE); g_signal_connect(app, "activate", G_CALLBACK(activate_wizard), (gpointer)&expert_mode); g_signal_connect(app, "startup", G_CALLBACK(startup_wizard), NULL); /* Enter main loop */ g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); if (opts & OPT_d) delete_dump_dir_possibly_using_abrtd(g_dump_dir_name); save_user_settings(); 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_template = _( "& [-vsoxtf] [-e]/[-c CURSOR] [-d DIR]/[-D]\n" "\n" "Extract Xorg crash from systemd-journal\n" "\n" "-c and -e options conflicts because both specifies the first read message.\n" "\n" "-e is useful only for -f because the following of journal starts by reading \n" "the entire journal if the last seen possition is not available.\n" "\n" "The last seen position is saved in %s\n" "\n" "Journal filter is required parameter and must be specified either by parameter\n" "-j or in %s conf file.\n" ); char program_usage_string[strlen(program_usage_string_template) + strlen(ABRT_JOURNAL_XORG_WATCH_STATE_FILE) + strlen(XORG_CONF_PATH)]; sprintf(program_usage_string, program_usage_string_template, ABRT_JOURNAL_XORG_WATCH_STATE_FILE, XORG_CONF_PATH); enum { OPT_v = 1 << 0, OPT_s = 1 << 1, OPT_o = 1 << 2, OPT_d = 1 << 3, OPT_D = 1 << 4, OPT_x = 1 << 5, OPT_t = 1 << 6, OPT_c = 1 << 7, OPT_e = 1 << 8, OPT_f = 1 << 9, OPT_a = 1 << 10, OPT_J = 1 << 11, OPT_j = 1 << 12, }; char *cursor = NULL; char *dump_location = NULL; char *journal_dir = NULL; GList *journal_filters = NULL; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_BOOL( 's', NULL, NULL, _("Log to syslog")), OPT_BOOL( 'o', NULL, NULL, _("Print found crashes on standard output")), OPT_STRING('d', NULL, &dump_location, "DIR", _("Create new problem directory in DIR for every crash found")), OPT_BOOL( 'D', NULL, NULL, _("Same as -d DumpLocation, DumpLocation is specified in abrt.conf")), OPT_BOOL( 'x', NULL, NULL, _("Make the problem directory world readable")), OPT_BOOL( 't', NULL, NULL, _("Throttle problem directory creation to 1 per second")), OPT_STRING('c', NULL, &cursor, "CURSOR", _("Start reading systemd-journal from the CURSOR position")), OPT_BOOL( 'e', NULL, NULL, _("Start reading systemd-journal from the end")), OPT_BOOL( 'f', NULL, NULL, _("Follow systemd-journal from the last seen position (if available)")), OPT_BOOL( 'a', NULL, NULL, _("Read journal files from all machines")), OPT_STRING('J', NULL, &journal_dir, "PATH", _("Read all journal files from directory at PATH")), OPT_LIST( 'j', NULL, &journal_filters, "FILTER", _("Journal filter e.g. '_COMM=gdm-x-session' (may be given many times)")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); msg_prefix = g_progname; if ((opts & OPT_s) || getenv("ABRT_SYSLOG")) { logmode = LOGMODE_JOURNAL; } if ((opts & OPT_c) && (opts & OPT_e)) error_msg_and_die(_("You need to specify either -c CURSOR or -e")); if (opts & OPT_D) { if (opts & OPT_d) show_usage_and_die(program_usage_string, program_options); load_abrt_conf(); dump_location = g_settings_dump_location; g_settings_dump_location = NULL; free_abrt_conf_data(); } int xorg_utils_flags = 0; if ((opts & OPT_x)) xorg_utils_flags |= ABRT_XORG_WORLD_READABLE; if ((opts & OPT_t)) xorg_utils_flags |= ABRT_XORG_THROTTLE_CREATION; if ((opts & OPT_o)) xorg_utils_flags |= ABRT_XORG_PRINT_STDOUT; /* get journal filters */ const char *const env_journal_filter = getenv("ABRT_DUMP_JOURNAL_XORG_DEBUG_FILTER"); bool free_filter_list_data = false; GList *xorg_journal_filter = NULL; if (env_journal_filter != NULL) { xorg_journal_filter = g_list_append(xorg_journal_filter, (gpointer)env_journal_filter); log_debug("Using journal filter from environment variable"); } else if (journal_filters != NULL) { xorg_journal_filter = journal_filters; log_debug("Using journal filter passed by parameter -j"); } else { map_string_t *settings = new_map_string(); log_notice("Loading settings from '%s'", XORG_CONF); load_abrt_plugin_conf_file(XORG_CONF, settings); log_debug("Loaded '%s'", XORG_CONF); const char *conf_journal_filters = get_map_string_item_or_NULL(settings, "JournalFilters"); xorg_journal_filter = parse_list(conf_journal_filters); /* list data will be free by g_list_free_full */ free_filter_list_data = true; free_map_string(settings); if (xorg_journal_filter) log_debug("Using journal filter from conf file %s", XORG_CONF); } if (xorg_journal_filter == NULL) error_msg_and_die(_("Journal filter must be specified either by parameter -j or stored in /etc/abrt/plugins/xorg.conf file")); abrt_journal_t *journal = NULL; if ((opts & OPT_J)) { log_debug("Using journal files from directory '%s'", journal_dir); if (abrt_journal_open_directory(&journal, journal_dir)) error_msg_and_die(_("Cannot initialize systemd-journal in directory '%s'"), journal_dir); } else { if (((opts & OPT_a) ? abrt_journal_new_merged : abrt_journal_new)(&journal)) error_msg_and_die(_("Cannot open systemd-journal")); } if (abrt_journal_set_journal_filter(journal, xorg_journal_filter) < 0) error_msg_and_die(_("Cannot filter systemd-journal to Xorg data only")); /* free filter list */ if (free_filter_list_data) g_list_free_full(xorg_journal_filter, free); else g_list_free(xorg_journal_filter); if ((opts & OPT_e) && abrt_journal_seek_tail(journal) < 0) error_msg_and_die(_("Cannot seek to the end of journal")); if ((opts & OPT_f)) { if (!cursor) abrt_journal_restore_position(journal, ABRT_JOURNAL_XORG_WATCH_STATE_FILE); else if(abrt_journal_set_cursor(journal, cursor)) error_msg_and_die(_("Failed to start watch from cursor '%s'"), cursor); watch_journald(journal, dump_location, xorg_utils_flags); abrt_journal_save_current_position(journal, ABRT_JOURNAL_XORG_WATCH_STATE_FILE); } else { if (cursor && abrt_journal_set_cursor(journal, cursor)) error_msg_and_die(_("Failed to set systemd-journal cursor '%s'"), cursor); /* Compatibility hack, a watch's callback gets the journal already moved * to a next message.*/ abrt_journal_next(journal); GList *crashes = abrt_journal_extract_xorg_crashes(journal); abrt_xorg_process_list_of_crashes(crashes, dump_location, xorg_utils_flags); g_list_free_full(crashes, (GDestroyNotify)xorg_crash_info_free); } abrt_journal_free(journal); return EXIT_SUCCESS; }
int main(int argc, char **argv) { abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif const char *program_usage_string = _( "\n& [-vf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR" "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..." "\nor:" "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w" "\nor:" "\n& [-v] [-c CONFFILE]... -h DUPHASH" "\n" "\nReports problem to MantisBT." "\n" "\nThe tool reads DIR. Then it tries to find an issue" "\nwith the same abrt_hash in custom field 'abrt_hash'." "\n" "\nIf such issue is not found, then a new issue is created. Elements of DIR" "\nare stored in the issue as part of issue description or as attachments," "\ndepending on their type and size." "\n" "\nOtherwise, if such issue is found and it is marked as CLOSED DUPLICATE," "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE issue." "\nThe tool adds a new comment to found issue." "\n" "\nThe URL to new or modified issue is printed to stdout and recorded in" "\n'reported_to' element." "\n" "\nOption -t uploads FILEs to the already created issue on MantisBT site." "\nThe issue ID is retrieved from directory specified by -d DIR." "\nIf problem data in DIR was never reported to MantisBT, upload will fail." "\n" "\nOption -tID uploads FILEs to the issue with specified ID on MantisBT site." "\n-d DIR is ignored." "\n" "\nOption -r sets the last url from reporter_to element which is prefixed with" "\nTRACKER_NAME to URL field. This option is applied only when a new issue is to be" "\nfiled. The default value is 'ABRT Server'" "\n" "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/mantisbt.conf" "\nand user's local ~"USER_HOME_CONFIG_PATH"/mantisbt.conf." "\nIts lines should have 'PARAM = VALUE' format." "\nRecognized string parameters: MantisbtURL, Login, Password, Project, ProjectVersion." "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate." "\nUser's local configuration overrides the system wide configuration." "\nParameters can be overridden via $Mantisbt_PARAM environment variables." "\n" "\nFMTFILE default to "CONF_DIR"/plugins/mantisbt_format.conf." "\nFMTFILE2 default to "CONF_DIR"/plugins/mantisbt_formatdup.conf." ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, OPT_F = 1 << 3, OPT_A = 1 << 4, OPT_t = 1 << 5, OPT_f = 1 << 6, OPT_h = 1 << 7, OPT_r = 1 << 8, OPT_D = 1 << 9, }; const char *dump_dir_name = "."; GList *conf_file = NULL; const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf"; const char *fmt_file2 = CONF_DIR"/plugins/mantisbt_formatdup.conf"; char *abrt_hash = NULL; char *ticket_no = NULL; const char *tracker_str = "ABRT Server"; char *debug_str = NULL; mantisbt_settings_t mbt_settings = { 0 }; /* 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_LIST( 'c', NULL, &conf_file , "FILE" , _("Configuration file (may be given many times)")), OPT_STRING( 'F', NULL, &fmt_file , "FILE" , _("Formatting file for initial comment")), OPT_STRING( 'A', NULL, &fmt_file2 , "FILE" , _("Formatting file for duplicates")), OPT_OPTSTRING('t', "ticket", &ticket_no , "ID" , _("Attach FILEs [to issue with this ID]")), OPT_BOOL( 'f', NULL, NULL, _("Force reporting even if this problem is already reported")), OPT_STRING( 'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")), OPT_STRING( 'r', "tracker", &tracker_str, "TRACKER_NAME", _("A name of bug tracker for an additional URL from 'reported_to'")), OPT_OPTSTRING('D', "debug", &debug_str , "STR" , _("Debug")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); argv += optind; export_abrt_envvars(0); map_string_t *settings = new_map_string(); { char *local_conf = NULL; if (!conf_file) { conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf"); local_conf = xasprintf("%s"USER_HOME_CONFIG_PATH"/mantisbt.conf", getenv("HOME")); conf_file = g_list_append(conf_file, local_conf); } while (conf_file) { char *fn = (char *)conf_file->data; log_notice("Loading settings from '%s'", fn); load_conf_file(fn, settings, /*skip key w/o values:*/ false); log_debug("Loaded '%s'", fn); conf_file = g_list_delete_link(conf_file, conf_file); } free(local_conf); struct dump_dir *dd = NULL; if (abrt_hash == NULL) { dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) error_msg_and_die(_("Can't open problem dir '%s'."), dump_dir_name); } set_settings(&mbt_settings, settings, dd); dd_close(dd); /* WRONG! set_settings() does not copy the strings, it merely sets up pointers * to settings[] dictionary: */ /*free_map_string(settings);*/ } /* No connection is opened between client and server. Users authentication * is performed on every SOAP method call. In the first step we verify the * credentials by calling 'mc_login' method. In the case the credentials are * correctly applies the reporter uses them in the next requests. It is not * necessary to call 'mc_login' method because the method provides only * verification of credentials. */ verify_credentials(&mbt_settings); if (abrt_hash) { log_warning(_("Looking for similar problems in MantisBT")); GList *ids = mantisbt_search_by_abrt_hash(&mbt_settings, abrt_hash); mantisbt_settings_free(&mbt_settings); if (ids == NULL) return EXIT_FAILURE; puts(ids->data); response_values_free(ids); return EXIT_SUCCESS; } mantisbt_get_project_id_from_name(&mbt_settings); if (opts & OPT_t) { if (!argv[0]) show_usage_and_die(program_usage_string, program_options); if (!ticket_no) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "MantisBT"); dd_close(dd); if (!reported_to || !reported_to->url) error_msg_and_die(_("Can't get MantisBT ID because this problem has not yet been reported to MantisBT.")); char *url = reported_to->url; reported_to->url = NULL; free_report_result(reported_to); if (prefixcmp(url, mbt_settings.m_mantisbt_url) != 0) error_msg_and_die(_("This problem has been reported to MantisBT '%s' which differs from the configured MantisBT '%s'."), url, mbt_settings.m_mantisbt_url); ticket_no = strrchr(url, '='); if (!ticket_no) error_msg_and_die(_("Malformed url to MantisBT '%s'."), url); /* won't ever call free on it - it simplifies the code a lot */ ticket_no = xstrdup(ticket_no + 1); log_warning(_("Using MantisBT ID '%s'"), ticket_no); } /* Attach files to existing MantisBT issues */ while (*argv) { const char *path = *argv++; char *filename = basename(path); log_warning(_("Attaching file '%s' to issue %s"), filename, ticket_no); mantisbt_attach_file(&mbt_settings, ticket_no, filename, path); } return 0; } /* Create new issue in MantisBT */ if (!(opts & OPT_f)) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); report_result_t *reported_to = find_in_reported_to(dd, "MantisBT"); dd_close(dd); if (reported_to && reported_to->url) { char *msg = xasprintf(_("This problem was already reported to MantisBT (see '%s')." " Do you still want to create a new issue?"), reported_to->url); int yes = ask_yes_no(msg); free(msg); if (!yes) return 0; } free_report_result(reported_to); } problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name); if (!problem_data) xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */ const char *category = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT); const char *duphash = problem_data_get_content_or_die(problem_data, FILENAME_DUPHASH); if (opts & OPT_D) { problem_formatter_t *pf = problem_formatter_new(); problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, /* optional section */ 0); if (problem_formatter_load_file(pf, fmt_file)) error_msg_and_die("Invalid format file: %s", fmt_file); problem_report_t *pr = NULL; if (problem_formatter_generate_report(pf, problem_data, &pr)) error_msg_and_die("Failed to format issue report from problem data"); printf("summary: %s\n" "\n" "Description:\n%s\n" "Additional info:\n%s\n" , problem_report_get_summary(pr) , problem_report_get_description(pr) , problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO) ); puts("attachments:"); for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) printf(" %s\n", (const char *)a->data); problem_report_free(pr); problem_formatter_free(pf); exit(0); } unsigned long bug_id = 0; /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345 * and won't search on MantisBT server. */ char *remote_result; remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT); if (remote_result) { char *cmd = strtok(remote_result, " \n"); char *id = strtok(NULL, " \n"); if (!prefixcmp(cmd, "DUPLICATE")) { errno = 0; char *e; bug_id = strtoul(id, &e, 10); if (errno || id == e || *e != '\0' || bug_id > INT_MAX) { /* error / no digits / illegal trailing chars / too big a number */ bug_id = 0; } } } mantisbt_issue_info_t *ii; if (!bug_id) { log_warning(_("Checking for duplicates")); int existing_id = -1; int crossver_id = -1; { /* Figure out whether we want to match category * when doing dup search. */ const char *category_substitute = is_in_comma_separated_list(category, mbt_settings.m_DontMatchComponents) ? NULL : category; /* We don't do dup detection across versions (see below why), * but we do add a note if cross-version potential dup exists. * For that, we search for cross version dups first: */ // SOAP API searching method is not in the final version, it's possible the project will be string GList *crossver_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, /*version*/ NULL, duphash); unsigned crossver_bugs_count = g_list_length(crossver_bugs_ids); log_debug("MantisBT has %i reports with duphash '%s' including cross-version ones", crossver_bugs_count, duphash); if (crossver_bugs_count > 0) crossver_id = atoi(g_list_first(crossver_bugs_ids)->data); if (crossver_bugs_count > 0) { // SOAP API searching method is not in the final version, it's possible the project will be string GList *dup_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, mbt_settings.m_project_version, duphash); unsigned dup_bugs_count = g_list_length(dup_bugs_ids); log_debug("MantisBT has %i reports with duphash '%s'", dup_bugs_count, duphash); if (dup_bugs_count > 0) existing_id = atoi(g_list_first(dup_bugs_ids)->data); } } if (existing_id < 0) { /* Create new issue */ log_warning(_("Creating a new issue")); problem_formatter_t *pf = problem_formatter_new(); problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, 0); if (problem_formatter_load_file(pf, fmt_file)) error_msg_and_die(_("Invalid format file: %s"), fmt_file); problem_report_t *pr = NULL; if (problem_formatter_generate_report(pf, problem_data, &pr)) error_msg_and_die(_("Failed to format problem data")); if (crossver_id >= 0) problem_report_buffer_printf( problem_report_get_buffer(pr, PR_SEC_DESCRIPTION), "\nPotential duplicate: issue %u\n", crossver_id); problem_formatter_free(pf); /* get tracker URL if exists */ struct dump_dir *dd = dd_opendir(dump_dir_name, 0); char *tracker_url = NULL; if (dd) { report_result_t *reported_to = find_in_reported_to(dd, tracker_str); dd_close(dd); if (reported_to && reported_to->url) { log_warning(_("Adding External URL to issue")); tracker_url = xstrdup(reported_to->url); free_report_result(reported_to); } } int new_id = mantisbt_create_new_issue(&mbt_settings, problem_data, pr, tracker_url); free(tracker_url); if (new_id == -1) return EXIT_FAILURE; log_warning(_("Adding attachments to issue %i"), new_id); char *new_id_str = xasprintf("%u", new_id); for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) { const char *item_name = (const char *)a->data; struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name); if (!item) continue; else if (item->flags & CD_FLAG_TXT) mantisbt_attach_data(&mbt_settings, new_id_str, item_name, item->content, strlen(item->content)); else if (item->flags & CD_FLAG_BIN) mantisbt_attach_file(&mbt_settings, new_id_str, item_name, item->content); } free(new_id_str); problem_report_free(pr); ii = mantisbt_issue_info_new(); ii->mii_id = new_id; ii->mii_status = xstrdup("new"); goto finish; } bug_id = existing_id; } ii = mantisbt_get_issue_info(&mbt_settings, bug_id); log_warning(_("Bug is already reported: %i"), ii->mii_id); /* Follow duplicates */ if ((strcmp(ii->mii_status, "closed") == 0) && (strcmp(ii->mii_resolution, "duplicate") == 0) ) { mantisbt_issue_info_t *origin = mantisbt_find_origin_bug_closed_duplicate(&mbt_settings, ii); if (origin) { mantisbt_issue_info_free(ii); ii = origin; } } /* TODO CC list * Is no MantisBT SOAP API method which allows adding users to CC list * without updating issue. */ /* Add comment and bt */ const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT); if (comment && comment[0]) { problem_formatter_t *pf = problem_formatter_new(); if (problem_formatter_load_file(pf, fmt_file2)) error_msg_and_die(_("Invalid duplicate format file: '%s"), fmt_file2); problem_report_t *pr; if (problem_formatter_generate_report(pf, problem_data, &pr)) error_msg_and_die(_("Failed to format duplicate comment from problem data")); const char *mbtcomment = problem_report_get_description(pr); int dup_comment = is_comment_dup(ii->mii_notes, mbtcomment); if (!dup_comment) { log_warning(_("Adding new comment to issue %d"), ii->mii_id); mantisbt_add_issue_note(&mbt_settings, ii->mii_id, mbtcomment); const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); unsigned rating = 0; const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING); /* python doesn't have rating file */ if (rating_str) rating = xatou(rating_str); if (bt && rating > ii->mii_best_bt_rating) { char *bug_id_str = xasprintf("%i", ii->mii_id); log_warning(_("Attaching better backtrace")); // find unique filename of attachment char *name = NULL; for (int i = 0;; ++i) { if (i == 0) name = xasprintf("%s", FILENAME_BACKTRACE); else name = xasprintf("%s%d", FILENAME_BACKTRACE, i); if (g_list_find_custom(ii->mii_attachments, name, (GCompareFunc) strcmp) == NULL) break; free(name); } mantisbt_attach_data(&mbt_settings, bug_id_str, name, bt, strlen(bt)); free(name); free(bug_id_str); } } else log_warning(_("Found the same comment in the issue history, not adding a new one")); problem_report_free(pr); problem_formatter_free(pf); } finish: log_warning(_("Status: %s%s%s %s/view.php?id=%u"), ii->mii_status, ii->mii_resolution ? " " : "", ii->mii_resolution ? ii->mii_resolution : "", mbt_settings.m_mantisbt_url, ii->mii_id); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (dd) { report_result_t rr = { .label = (char *)"MantisBT" }; rr.url = xasprintf("%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id); add_reported_to_entry(dd, &rr); free(rr.url); dd_close(dd); } mantisbt_settings_free(&mbt_settings); return 0; }
static void report_to_kerneloops( const char *dump_dir_name, map_string_t *settings) { problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name); if (!problem_data) xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */ const char *backtrace = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); if (!backtrace) error_msg_and_die("Error sending kernel oops due to missing backtrace"); const char *env = getenv("KerneloopsReporter_SubmitURL"); const char *submitURL = (env ? env : get_map_string_item_or_empty(settings, "SubmitURL")); if (!submitURL[0]) submitURL = "http://oops.kernel.org/submitoops.php"; log(_("Submitting oops report to %s"), submitURL); CURLcode ret = http_post_to_kerneloops_site(submitURL, backtrace); if (ret != CURLE_OK) error_msg_and_die("Kernel oops has not been sent due to %s", curl_easy_strerror(ret)); problem_data_free(problem_data); /* Server replies with: * 200 thank you for submitting the kernel oops information * RemoteIP: 34192fd15e34bf60fac6a5f01bba04ddbd3f0558 * - no URL or bug ID apparently... */ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (dd) { report_result_t rr = { .label = (char *)"kerneloops" }; rr.url = (char *)submitURL; add_reported_to_entry(dd, &rr); dd_close(dd); } log("Kernel oops report was uploaded"); } int main(int argc, char **argv) { abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif map_string_t *settings = new_map_string(); const char *dump_dir_name = "."; GList *conf_file = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [-v] [-c CONFFILE]... -d DIR\n" "\n" "Reports kernel oops to kerneloops.org (or similar) site.\n" "\n" "Files with names listed in $EXCLUDE_FROM_REPORT are not included\n" "into the tarball.\n" "\n" "CONFFILE lines should have 'PARAM = VALUE' format.\n" "Recognized string parameter: SubmitURL.\n" "Parameter can be overridden via $KerneloopsReporter_SubmitURL." ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, }; /* 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_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); while (conf_file) { char *fn = (char *)conf_file->data; log_notice("Loading settings from '%s'", fn); load_conf_file(fn, settings, /*skip key w/o values:*/ false); log_debug("Loaded '%s'", fn); conf_file = g_list_remove(conf_file, fn); } report_to_kerneloops(dump_dir_name, settings); free_map_string(settings); return 0; }