static void generate_fingerprints(struct disasm_data ddata, GHashTable *plt_names, GHashTable *call_graph, GList *entries) { GList *it; GList *insns; struct backtrace_entry *entry; fp_function_type *fp; for (it = entries; it != NULL; it = g_list_next(it)) { entry = it->data; uintptr_t function_begin = entry->function_initial_loc; uintptr_t function_end = function_begin + entry->function_length; insns = disasm_function(ddata, function_begin, function_end); if (insns == NULL) { VERB1 log("Cannot disassemble function at 0x%jx, not computing fingerprint", (uintmax_t)function_begin); continue; } struct btp_strbuf *strbuf = btp_strbuf_new(); btp_strbuf_append_strf(strbuf, "v1"); /* Fingerprint version */ for (fp = fp_components; *fp != NULL; fp++) { btp_strbuf_append_strf(strbuf, ";"); (*fp)(strbuf, insns, function_begin, function_end, call_graph, plt_names); } list_free_with_free(insns); entry->fingerprint = btp_strbuf_free_nobuf(strbuf); } }
CURLcode curl_easy_perform_with_proxy(CURL *handle, const char *url) { GList *proxy_list, *li; CURLcode curl_err; proxy_list = get_proxy_list(url); if (proxy_list) { /* Try with each proxy before giving up. */ /* TODO: Should we repeat the perform call only on certain errors? */ for (li = proxy_list, curl_err = 1; curl_err && li; li = g_list_next(li)) { xcurl_easy_setopt_ptr(handle, CURLOPT_PROXY, li->data); VERB1 log("Connecting to %s (using proxy server %s)", url, (const char *)li->data); curl_err = curl_easy_perform(handle); } } else { VERB1 log("Connecting to %s", url); curl_err = curl_easy_perform(handle); } list_free_with_free(proxy_list); return curl_err; }
void rpm_init() { #ifdef HAVE_LIBRPM if (rpmReadConfigFiles(NULL, NULL) != 0) error_msg("Can't read RPM rc files"); #endif list_free_with_free(list_fingerprints); /* paranoia */ /* Huh? Why do we start the list with an element with NULL string? */ list_fingerprints = g_list_alloc(); }
static void free_rule_list(GList *rule_list) { while (rule_list) { struct rule *cur_rule = rule_list->data; list_free_with_free(cur_rule->conditions); free(cur_rule->command); free(cur_rule); GList *next = rule_list->next; g_list_free_1(rule_list); rule_list = next; } }
static void fp_calltree_leaves(struct btp_strbuf *buffer, GList *insns, uintptr_t begin, uintptr_t end, GHashTable *call_graph, GHashTable *plt) { unsigned iterations_allowed = call_graph_iteration_limit; GHashTable *visited = g_hash_table_new_full(g_int64_hash, g_int64_equal, free, NULL); GList *queue = g_list_append(NULL, addr_alloc(begin)); GList *it, *sym = NULL; while (queue != NULL && iterations_allowed) { /* Pop one element */ it = g_list_first(queue); queue = g_list_remove_link(queue, it); uintptr_t *key = (uintptr_t*)(it->data); /* uintptr_t addr = *key; */ g_list_free(it); iterations_allowed--; /* Check if it is not already visited */ if (g_hash_table_lookup_extended(visited, key, NULL, NULL)) { free(key); continue; } g_hash_table_insert(visited, key, key); /* Lookup callees */ GList *callees = g_hash_table_lookup(call_graph, key); /* If callee is PLT, add the corresponding symbols, otherwise * extend the worklist */ for (it = callees; it != NULL; it = g_list_next(it)) { char *s = g_hash_table_lookup(plt, it->data); if (s && !g_list_find_custom(sym, s, (GCompareFunc)strcmp)) { sym = g_list_insert_sorted(sym, s, (GCompareFunc)strcmp); } else if (s == NULL) { queue = g_list_append(queue, addr_alloc(*(uintptr_t*)(it->data))); } } } g_hash_table_destroy(visited); list_free_with_free(queue); fingerprint_add_list(buffer, "calltree_leaves", sym); }
static void free_bodhi_item(struct bodhi *b) { if (!b) return; free(b->nvr); #if 0 list_free_with_free(b->bz_ids); free(b->date_pushed); free(b->status); free(b->dist_tag); #endif free(b); }
void free_bug_info(struct bug_info *bi) { func_entry(); if (!bi) return; free(bi->bi_status); free(bi->bi_resolution); free(bi->bi_reporter); free(bi->bi_product); list_free_with_free(bi->bi_cc_list); free(bi); }
void rpm_destroy() { #ifdef HAVE_LIBRPM /* Mirroring the order of deinit calls in rpm-4.11.1/lib/poptALL.c::rpmcliFini() */ rpmFreeCrypto(); rpmFreeMacros(NULL); rpmFreeRpmrc(); /* rpm >= 4.14 handles this automatically on exit */ #if 0 /* RPM doc says "clean up any open iterators and databases". * Observed to eliminate these Berkeley DB warnings: * "BDB2053 Freeing read locks for locker 0x1e0: 28718/139661746636736" */ rpmdbCheckTerminate(1); #endif #endif list_free_with_free(list_fingerprints); list_fingerprints = NULL; }
GList *load_words_from_file(const char* filename) { GList *words_list = NULL; GList *file_list = NULL; file_list = g_list_prepend(file_list, concat_path_file(CONF_DIR, filename)); file_list = g_list_prepend(file_list, get_user_config_file_path(filename, /*don't append suffix*/NULL)); GList *file_list_cur = file_list; while(file_list_cur) { char *cur_file = (char *)file_list_cur->data; FILE *fp = fopen(cur_file, "r"); if (fp) { /* every line is one word */ char *line; while ((line = xmalloc_fgetline(fp)) != NULL) { //FIXME: works only if the '#' is first char won't work for " #abcd# if (line[0] != '#') // if it's not comment words_list = g_list_append(words_list, line); else free(line); } fclose(fp); } else { log_warning("Can't open %s", cur_file); } file_list_cur = g_list_next(file_list_cur); } list_free_with_free(file_list); return words_list; }
static void delete_files(gpointer data, gpointer void_preserve_list) { double cap_size; const char *dir = parse_size_pfx(&cap_size, data); GList *preserve_files_list = void_preserve_list; unsigned count = 100; while (--count != 0) { GList *worst_file_list = NULL; double cur_size = get_dir_size(dir, &worst_file_list, preserve_files_list); if (cur_size <= cap_size || !worst_file_list) { list_free_with_free(worst_file_list); log_info("cur_size:%.0f cap_size:%.0f, no (more) trimming", cur_size, cap_size); break; } /* Invert the list, so that largest/oldest file is first */ worst_file_list = g_list_reverse(worst_file_list); /* And delete (some of) them */ while (worst_file_list && cur_size > cap_size) { struct name_and_size *ns = worst_file_list->data; log_notice("%s is %.0f bytes (more than %.0f MB), deleting '%s' (%llu bytes)", dir, cur_size, cap_size / (1024*1024), ns->name, (long long)ns->size); if (unlink(ns->name) != 0) perror_msg("Can't unlink '%s'", ns->name); else cur_size -= ns->size; free(ns); worst_file_list = g_list_delete_link(worst_file_list, worst_file_list); } } }
/* Load ELF 'filename', parse the .eh_frame contents, and for each entry in the * second argument check whether its address is contained in the range of some * Frame Description Entry. If it does, fill in the function range of the * entry. In other words, try to assign start address and length of function * corresponding to each backtrace entry. We'll need that for the disassembly. * * Fails quietly - we should still be able to use the build ids. * * I wonder if this is really better than parsing eu-readelf text output. */ static GHashTable *elf_iterate_fdes(const char *filename, GList *entries, Elf *e) { const unsigned char *e_ident; Elf_Data *scn_data; GElf_Shdr shdr; GElf_Phdr phdr; size_t phnum; GHashTable *retval = NULL; /* NULL = error */ e_ident = (unsigned char *)elf_getident(e, NULL); if (e_ident == NULL) { VERB1 log_elf_error("elf_getident", filename); return NULL; } /* Look up the .eh_frame section */ if (!xelf_section_by_name(e, ".eh_frame", filename, &scn_data, &shdr)) { VERB1 log("Section .eh_frame not found in %s", filename); return NULL; } /* Get the address at which the executable segment is loaded. If the * .eh_frame addresses are absolute, this is used to convert them to * relative to the beginning of executable segment. We are looking for the * first LOAD segment that is executable, I hope this is sufficient. */ if (elf_getphdrnum(e, &phnum) != 0) { VERB1 log_elf_error("elf_getphdrnum", filename); return NULL; } uintptr_t exec_base; int i; for (i = 0; i < phnum; i++) { if (gelf_getphdr(e, i, &phdr) != &phdr) { VERB1 log_elf_error("gelf_getphdr", filename); return NULL; } if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) { exec_base = (uintptr_t)phdr.p_vaddr; goto base_found; } } VERB1 log("Can't determine executable base for '%s'", filename); return NULL; base_found: VERB2 log("Executable base: %jx", (uintmax_t)exec_base); /* We now have a handle to .eh_frame data. We'll use dwarf_next_cfi to * iterate through all FDEs looking for those matching the addresses we * have. * Some info on .eh_frame can be found at http://www.airs.com/blog/archives/460 * and in DWARF documentation for .debug_frame. The initial_location and * address_range decoding is 'inspired' by elfutils source. * XXX: If this linear scan is too slow, we can do binary search on * .eh_frame_hdr -- see http://www.airs.com/blog/archives/462 */ int ret; Dwarf_Off cfi_offset; Dwarf_Off cfi_offset_next = 0; Dwarf_CFI_Entry cfi; struct cie_encoding { Dwarf_Off cie_offset; int ptr_len; bool pcrel; } *cie; GList *cie_list = NULL; /* Init hash table * keys are pointers to integers which we allocate with malloc * values stored directly */ GHashTable *hash = g_hash_table_new_full(g_int64_hash, g_int64_equal, free, NULL); while(1) { cfi_offset = cfi_offset_next; ret = dwarf_next_cfi(e_ident, scn_data, 1, cfi_offset, &cfi_offset_next, &cfi); if (ret > 0) { /* We're at the end. */ break; } if (ret < 0) { /* Error. If cfi_offset_next was updated, we may skip the * erroneous cfi. */ if (cfi_offset_next > cfi_offset) { continue; } VERB1 log("dwarf_next_cfi failed for %s: %s", filename, dwarf_errmsg(-1)); goto ret_free; } if (dwarf_cfi_cie_p(&cfi)) { /* Current CFI is a CIE. We store its offset and FDE encoding * attributes to be used when reading FDEs. */ /* Default FDE encoding (i.e. no R in augmentation string) is * DW_EH_PE_absptr. */ cie = btp_mallocz(sizeof(*cie)); cie->cie_offset = cfi_offset; cie->ptr_len = encoded_size(DW_EH_PE_absptr, e_ident); /* Search the augmentation data for FDE pointer encoding. * Unfortunately, 'P' can come before 'R' (which we are looking * for), so we may have to parse the whole thing. See the * abovementioned blog post for details. */ const char *aug = cfi.cie.augmentation; const uint8_t *augdata = cfi.cie.augmentation_data; bool skip_cie = 0; if (*aug == 'z') { aug++; } while (*aug != '\0') { if(*aug == 'R') { cie->ptr_len = encoded_size(*augdata, e_ident); if (cie->ptr_len != 4 && cie->ptr_len != 8) { VERB1 log("Unknown FDE encoding (CIE %jx) in %s", (uintmax_t)cfi_offset, filename); skip_cie = 1; } if ((*augdata & 0x70) == DW_EH_PE_pcrel) { cie->pcrel = 1; } break; } else if (*aug == 'L') { augdata++; } else if (*aug == 'P') { unsigned size = encoded_size(*augdata, e_ident); if (size == 0) { VERB1 log("Unknown size for personality encoding in %s", filename); skip_cie = 1; break; } augdata += (size + 1); } else { VERB1 log("Unknown augmentation char in %s", filename); skip_cie = 1; break; } aug++; } if (skip_cie) { free(cie); continue; } cie_list = g_list_append(cie_list, cie); } else { /* Current CFI is an FDE. */ GList *it = cie_list; cie = NULL; /* Find the CIE data that we should have saved earlier. XXX: We can * use hash table/tree to speed up the search, the number of CIEs * should usally be very low though. */ while (it != NULL) { cie = it->data; /* In .eh_frame, CIE_pointer is relative, but libdw converts it * to absolute offset. */ if(cfi.fde.CIE_pointer == cie->cie_offset) { break; /* Found. */ } it = g_list_next(it); } if (it == NULL) { VERB1 log("CIE not found for FDE %jx in %s", (uintmax_t)cfi_offset, filename); continue; } /* Read the two numbers we need and if they are PC-relative, * compute the offset from VMA base */ uintptr_t initial_location = fde_read_address(cfi.fde.start, cie->ptr_len); uintptr_t address_range = fde_read_address(cfi.fde.start+cie->ptr_len, cie->ptr_len); if (cie->pcrel) { /* We need to determine how long is the 'length' (and * consequently CIE id) field of this FDE -- it can be either 4 * or 12 bytes long. */ uintptr_t length = fde_read_address(scn_data->d_buf + cfi_offset, 4); uintptr_t skip = (length == 0xffffffffUL ? 12 : 4); uintptr_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL); initial_location += (uintptr_t)shdr.sh_offset + (uintptr_t)cfi_offset + 2*skip; initial_location &= mask; } else { /* Assuming that not pcrel means absolute address (what if the file is a library?). * Convert to text-section-start-relative. */ initial_location -= exec_base; } /* Insert the pair into hash */ uintptr_t *key = addr_alloc(initial_location + exec_base); g_hash_table_insert(hash, key, (gpointer)address_range); VERB3 log("FDE start: 0x%jx length: %u", (uintmax_t)*key, (unsigned)address_range); /* Iterate through the backtrace entries and check each address * member whether it belongs into the range given by current FDE. */ for (it = entries; it != NULL; it = g_list_next(it)) { struct backtrace_entry *entry = it->data; if (initial_location <= entry->build_id_offset && entry->build_id_offset < initial_location + address_range) { /* Convert to before-relocation absolute addresses, disassembler uses those. */ entry->function_initial_loc = exec_base + initial_location; entry->function_length = address_range; /*TODO: remove the entry from the list to save a bit of time in next iteration?*/ } } } } retval = hash; /* success */ ret_free: list_free_with_free(cie_list); if (retval == NULL) g_hash_table_destroy(hash); return retval; }
/* Compute intra-module call graph (that is, only calls within the binary). */ static GHashTable* compute_call_graph(struct disasm_data data, GHashTable *fdes, GList *entries) { unsigned iterations_allowed = call_graph_iteration_limit; GList *it, *insns; struct backtrace_entry *entry; GList *queue = NULL; /* Keys are pointers to addresses, values are GLists of pointers to addresses. */ GHashTable *succ = g_hash_table_new_full(g_int64_hash, g_int64_equal, free, (GDestroyNotify)list_free_with_free); /* Seed the queue with functions from entries */ for (it = entries; it != NULL; it = g_list_next(it)) { entry = it->data; uintptr_t *k = addr_alloc(entry->function_initial_loc); queue = g_list_append(queue, k); } /* Note: allocated addresses that belong to the queue must either be * 'reassigned' to succ or freed. */ while (queue != NULL && iterations_allowed) { /* Pop one item from the queue */ it = g_list_first(queue); queue = g_list_remove_link(queue, it); uintptr_t *key = (uintptr_t*)(it->data); uintptr_t function_begin = *key; g_list_free(it); iterations_allowed--; /* Check if it is not already processed */ if (g_hash_table_lookup_extended(succ, key, NULL, NULL)) { free(key); continue; } /* Look up function length in fdes * note: length is stored casted to pointer */ uintptr_t p = (uintptr_t)g_hash_table_lookup(fdes, key); if (p == 0) { VERB3 log("Range not present for 0x%jx, skipping disassembly", (uintmax_t)function_begin); /* Insert empty list of callees so that we avoid looping infinitely */ g_hash_table_insert(succ, key, NULL); continue; } uintptr_t function_end = function_begin + p; /* Disassemble function */ insns = disasm_function(data, function_begin, function_end); if (insns == NULL) { VERB2 log("Disassembly of 0x%jx-0x%jx failed", (uintmax_t)function_begin, (uintmax_t)function_end); /* Insert empty list of callees so that we avoid looping infinitely */ g_hash_table_insert(succ, key, NULL); continue; } /* Scan for callees */ GList *fn_callees = callees(insns); list_free_with_free(insns); VERB3 log("Callees of 0x%jx:", (uintmax_t)function_begin); /* Insert callees to the workqueue */ for (it = fn_callees; it != NULL; it = g_list_next(it)) { uintptr_t c = *(uintptr_t*)it->data; queue = g_list_append(queue, addr_alloc(c)); VERB3 log("\t0x%jx", (uintmax_t)c); } /* Insert it to the hash so we don't have to recompute it */ g_hash_table_insert(succ, key, fn_callees); } return succ; }
void koops_extract_oopses(GList **oops_list, char *buffer, size_t buflen) { char *c; int linecount = 0; int lines_info_size = 0; struct abrt_koops_line_info *lines_info = NULL; /* Split buffer into lines */ if (buflen != 0) buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */ c = buffer; while (c < buffer + buflen) { char linelevel; char *c9; char *colon; linecount++; c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */ assert(c9); *c9 = '\0'; /* turn the \n into a string termination */ if (c9 == c) goto next_line; /* Is it a syslog file (/var/log/messages or similar)? * Even though _usually_ it looks like "Nov 19 12:34:38 localhost kernel: xxx", * some users run syslog in non-C locale: * "2010-02-22T09:24:08.156534-08:00 gnu-4 gnome-session[2048]: blah blah" * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ !!! * We detect it by checking for N:NN:NN pattern in first 15 chars * (and this still is not good enough... false positive: "pci 0000:15:00.0: PME# disabled") */ colon = strchr(c, ':'); if (colon && colon > c && colon < c + 15 && isdigit(colon[-1]) /* N:... */ && isdigit(colon[1]) /* ...N:NN:... */ && isdigit(colon[2]) && colon[3] == ':' && isdigit(colon[4]) /* ...N:NN:NN... */ && isdigit(colon[5]) ) { /* It's syslog file, not a bare dmesg */ /* Skip non-kernel lines */ char *kernel_str = strstr(c, "kernel: "); if (!kernel_str) { /* if we see our own marker: * "hostname abrt: Kerneloops: Reported 1 kernel oopses to Abrt" * we know we submitted everything upto here already */ if (strstr(c, "kernel oopses to Abrt")) { log_debug("Found our marker at line %d", linecount); free(lines_info); lines_info = NULL; lines_info_size = 0; list_free_with_free(*oops_list); *oops_list = NULL; } goto next_line; } c = kernel_str + sizeof("kernel: ")-1; } /* store and remove kernel log level */ linelevel = koops_line_skip_level((const char **)&c); koops_line_skip_jiffies((const char **)&c); if ((lines_info_size & 0xfff) == 0) { lines_info = xrealloc(lines_info, (lines_info_size + 0x1000) * sizeof(lines_info[0])); } lines_info[lines_info_size].ptr = c; lines_info[lines_info_size].level = linelevel; lines_info_size++; next_line: c = c9 + 1; } koops_extract_oopses_from_lines(oops_list, lines_info, lines_info_size); free(lines_info); }
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 = _( "& [-vusoxm] [-d DIR]/[-D] [FILE]\n" "\n" "Extract oops from FILE (or standard input)" ); enum { OPT_v = 1 << 0, OPT_s = 1 << 1, OPT_o = 1 << 2, OPT_d = 1 << 3, OPT_D = 1 << 4, OPT_u = 1 << 5, OPT_x = 1 << 6, OPT_t = 1 << 7, OPT_m = 1 << 8, }; char *problem_dir = NULL; char *dump_location = 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 oopses on standard output")), /* oopses don't contain any sensitive info, and even * the old koops app was showing the oopses to all users */ OPT_STRING('d', NULL, &dump_location, "DIR", _("Create new problem directory in DIR for every oops found")), OPT_BOOL( 'D', NULL, NULL, _("Same as -d DumpLocation, DumpLocation is specified in abrt.conf")), OPT_STRING('u', NULL, &problem_dir, "PROBLEM", _("Save the extracted information in PROBLEM")), 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_BOOL( 'm', NULL, NULL, _("Print search string(s) to stdout and exit")), 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_m) { char *oops_string_filter_regex = abrt_oops_string_filter_regex(); if (oops_string_filter_regex) { regex_t filter_re; if (regcomp(&filter_re, oops_string_filter_regex, REG_NOSUB) != 0) perror_msg_and_die(_("Failed to compile regex")); const regex_t *filter[] = { &filter_re, NULL }; koops_print_suspicious_strings_filtered(filter); regfree(&filter_re); free(oops_string_filter_regex); } else koops_print_suspicious_strings(); return 1; } 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 oops_utils_flags = 0; if ((opts & OPT_x)) oops_utils_flags |= ABRT_OOPS_WORLD_READABLE; if ((opts & OPT_t)) oops_utils_flags |= ABRT_OOPS_THROTTLE_CREATION; if ((opts & OPT_o)) oops_utils_flags |= ABRT_OOPS_PRINT_STDOUT; argv += optind; if (argv[0]) xmove_fd(xopen(argv[0], O_RDONLY), STDIN_FILENO); GList *oops_list = NULL; scan_syslog_file(&oops_list, STDIN_FILENO); unsigned errors = 0; if (opts & OPT_u) { log_warning("Updating problem directory"); switch (g_list_length(oops_list)) { case 0: { error_msg(_("Can't update the problem: no oops found")); errors = 1; break; } default: { log_notice(_("More oopses found: process only the first one")); } /* falls trought */ case 1: { struct dump_dir *dd = dd_opendir(problem_dir, /*open for writing*/0); if (dd) { abrt_oops_save_data_in_dump_dir(dd, (char *)oops_list->data, /*no proc modules*/NULL); dd_close(dd); } } } } else errors = abrt_oops_process_list(oops_list, dump_location, ABRT_DUMP_OOPS_ANALYZER, oops_utils_flags); list_free_with_free(oops_list); //oops_list = NULL; return errors; }
/* Checks rules in *pp_rule_list, starting from first (remaining) rule, * until it finds a rule with all conditions satisfied. * In this case, it deletes this rule and returns this rule's cmd. * Else (if it didn't find such rule), it returns NULL. * In case of error (dump_dir can't be opened), returns NULL. * * Intended usage: * list = load_rule_list(...); * while ((cmd = pop_next_command(&list, ...)) != NULL) * run(cmd); */ static char* pop_next_command(GList **pp_rule_list, char **pp_event_name, /* reports EVENT value thru this, if not NULL on entry */ struct dump_dir **pp_dd, /* use *pp_dd for access to dump dir, if non-NULL */ const char *dump_dir_name, const char *pfx, unsigned pfx_len ) { char *command = NULL; struct dump_dir *dd = pp_dd ? *pp_dd : NULL; GList *rule_list = *pp_rule_list; while (rule_list) { struct rule *cur_rule = rule_list->data; GList *condition = cur_rule->conditions; while (condition) { const char *cond_str = condition->data; const char *eq_sign = strchr(cond_str, '='); /* Is it "EVENT=foo"? */ if (strncmp(cond_str, "EVENT=", 6) == 0) { if (strncmp(eq_sign + 1, pfx, pfx_len) != 0) goto next_rule; /* prefix doesn't match */ if (pp_event_name) { free(*pp_event_name); *pp_event_name = xstrdup(eq_sign + 1); } } else { /* Read from dump dir and compare */ if (!dd) { /* Without dir to match, we assume match for all conditions */ if (!dump_dir_name) goto next_cond; dd = dd_opendir(dump_dir_name, /*flags:*/ DD_OPEN_READONLY); if (!dd) { free_rule_list(rule_list); *pp_rule_list = NULL; goto ret; /* error (note: dd_opendir logged error msg) */ } } /* Is it "VAR~=REGEX"? */ int regex = (eq_sign > cond_str && eq_sign[-1] == '~'); /* Is it "VAR!=VAL"? */ int inverted = (eq_sign > cond_str && eq_sign[-1] == '!'); char *var_name = xstrndup(cond_str, eq_sign - cond_str - (regex|inverted)); char *real_val = dd_load_text_ext(dd, var_name, DD_FAIL_QUIETLY_ENOENT); free(var_name); int vals_differ = regex ? regcmp_lines(real_val, eq_sign + 1) : strcmp(real_val, eq_sign + 1); free(real_val); if (inverted) vals_differ = !vals_differ; /* Do values match? */ if (vals_differ) /* no */ { //log_debug("var '%s': '%.*s'!='%s', skipping line", // p, // (int)(strchrnul(real_val, '\n') - real_val), real_val, // eq_sign); goto next_rule; } } next_cond: /* We are here if current condition is satisfied */ condition = condition->next; } /* while (condition) */ /* We are here if all conditions are satisfied */ /* IOW, we found rule to run, delete it and return its command */ *pp_rule_list = g_list_remove(*pp_rule_list, cur_rule); list_free_with_free(cur_rule->conditions); command = cur_rule->command; /*free(cur_rule->command); - WRONG! we are returning it! */ free(cur_rule); break; next_rule: rule_list = rule_list->next; } /* while (rule_list) */ ret: if (pp_dd) *pp_dd = dd; else dd_close(dd); return command; }