[ABRT PATCH v3 1/2] problem api: publish problem conditions
Jakub Filak
jfilak at redhat.com
Wed May 22 11:28:26 UTC 2013
and simplify count_problems_in_dir() helper
Related to #650
Signed-off-by: Jakub Filak <jfilak at redhat.com>
---
src/include/problem_api.h | 141 +++++++++++++++++++++++++++++---
src/lib/problem_api.c | 201 ++++++++++++++++++++++++++++++----------------
2 files changed, 265 insertions(+), 77 deletions(-)
diff --git a/src/include/problem_api.h b/src/include/problem_api.h
index 3b1dfd3..5daf0e4 100644
--- a/src/include/problem_api.h
+++ b/src/include/problem_api.h
@@ -23,22 +23,143 @@
/*
* Structure for simple conditions based on problem fields
*/
-struct problem_condition
-{
- /* a name of filed required by evaluate function */
- const char *field_name;
- /* extra data passed to evaluate function */
- const void *args;
- /* evaluate function returning TRUE if condition was passed */
- bool (*evaluate)(const char *, const void *);
-};
+struct problem_condition;
+
+/*
+ * Function pointer for evaluation function
+ *
+ * @param field_value Checked value
+ * @param args User arguments
+ */
+typedef bool (* problem_condition_evaluate)(const char *field_value, void *args);
+
+/*
+ * Function pointer for problem arguments destroy function
+ *
+ * @param args User arguments
+ */
+typedef void (* problem_condition_destroy_args)(void *args);
+
+/*
+ * Allocates and initialize a new problem condition
+ *
+ * @param field_name Name of tested element
+ * @param args Arguments passed to @evaluate function
+ * @param eval Condition evaluation function
+ * @param destroy Function to free the memory allocated for the args
+ */
+struct problem_condition *
+problem_condition_new(const char *field_name, void *args,
+ problem_condition_evaluate eval, problem_condition_destroy_args destroy);
+
+/*
+ * Evaluates a NULL-terminated list of problem conditions as a logical conjunction
+ *
+ * @param dd Data for evaluation
+ * @param condition NULL terminated list of condition
+ * @return True if @condition is satisfied by @dd
+ */
+bool
+problem_condition_evaluate_dump_dir_and(struct dump_dir *dd,
+ const struct problem_condition *const *condition);
+
+/*
+ * Evaluates a NULL-terminated list of problem conditions as a logical conjunction
+ *
+ * @param pd Data for evaluation
+ * @param condition NULL terminated list of condition
+ * @return True if @condition is satisfied by @pd
+ */
+bool
+problem_condition_evaluate_problem_data_and(problem_data_t *pd,
+ const struct problem_condition *const *condition);
+/*
+ * Frees the memory allocate for the condition
+ *
+ * @param cond Freed memory
+ */
+void
+problem_condition_free(struct problem_condition *cond);
+
+/*
+ * Allocates and initializes a new problem condition satisfied if value of @file_name
+ * is in range <@from, @to>
+ *
+ * @param file_name Name of tested element
+ * @param from First accepted timestamp of last occurrence of a problem
+ * @param to Last accepted timestamp of last occurrence of a problem. If 0 then current timestamp is being used.
+ * @return Mallocate memory which must be free by @problem_condition_free()
+ */
+struct problem_condition *
+time_interval_problem_condition_new(const char *file_name, unsigned long from, unsigned long to);
+
+/*
+ * Allocates and initializes a new problem condition satisfied if @file_name's
+ * value equals to @value
+ *
+ * @param file_name Name of tested element
+ * @param value Required value. Can be NULL
+ * @return Mallocate memory which must be free by @problem_condition_free()
+ */
+struct problem_condition *
+equal_element_value_problem_condition_new(const char *file_name, const char *value);
/* Retrieves the list of directories currently used as a problem storage
* The result must be freed by caller
* @returns List of strings representing the full path to dirs
*/
GList *get_problem_storages();
+
+/*
+ * Function pointer for callback in @foreach_problem_dir_in()
+ *
+ * @param dd Opened dump directory
+ * @param user_args User arguments
+ */
+typedef void (* dump_dir_callback)(struct dump_dir *dd, void *user_args);
+
+/*
+ * Goes through all problems and passes an opened dump directory to @callback
+ * function for problems accessible by caller_uid and problems for which
+ * @condition gets TRUE
+ *
+ * @param path Base dump directories location
+ * @param uid To filter out not accessible problems; -1 to disable this check
+ * @param condition a NULL-terminated list of problem conditions evaluated
+ * as conjunction, can be NULL (means always TRUE)
+ * @param callback Called for each accessible dump directory which satisfies @condition. Can be NULL
+ * @param user_data A user data for @callback
+ * @return Number of matching problem directories
+ */
+int foreach_problem_dir_matching(const char *path,
+ uid_t uid,
+ const struct problem_condition *const *condition,
+ dump_dir_callback callback,
+ void *user_data);
+
+/*
+ * Goes through all problems and selects only problems accessible by caller_uid and
+ * problems for which @condition gets TRUE
+ *
+ * @param condition a NULL-terminated list of problem conditions evaluated
+ * as conjunction, can be NULL (means always TRUE)
+ */
+GList *get_problem_dirs_for_cond(const char *path,
+ uid_t uid,
+ const struct problem_condition *const *condition);
+
GList *get_problem_dirs_for_uid(uid_t uid, const char *dump_location);
+
+/*
+ * Finds problems which were created in the interval and @element holds @value.
+ *
+ * @param uid To filter out not accessible problems; -1 to disable this check
+ * @param element File inside of dump directory. NULL to disable this check
+ * @param value Required value stored in @element
+ * @param timestamp_from First accepted timestamp of last occurrence of a problem
+ * @param timestamp_to Last accepted timestamp of last occurrence of a problem. If 0 then current timestamp is being used.
+ * @param path Base directory
+ */
GList *get_problem_dirs_for_element_in_time(uid_t uid,
const char *element,
const char *value,
@@ -52,4 +173,4 @@ GList *get_problem_dirs_for_element_in_time(uid_t uid,
* @since[in]
* @returns count of problems
*/
-unsigned int get_problems_count(GList *paths, unsigned long since);
\ No newline at end of file
+unsigned int get_problems_count(GList *paths, unsigned long since);
diff --git a/src/lib/problem_api.c b/src/lib/problem_api.c
index d940767..8128ed7 100644
--- a/src/lib/problem_api.c
+++ b/src/lib/problem_api.c
@@ -22,18 +22,63 @@
#include "problem_api.h"
/*
+ * Structure for simple conditions based on problem fields
+ */
+struct problem_condition
+{
+ /* a name of filed required by evaluate function */
+ char *field_name;
+ /* extra data passed to evaluate function */
+ void *args;
+ /* evaluate function returning TRUE if condition was passed */
+ problem_condition_evaluate evaluate;
+ /* args destroy function */
+ problem_condition_destroy_args destroy;
+};
+
+struct problem_condition *
+problem_condition_new(const char *field_name, void *args,
+ problem_condition_evaluate evaluate,
+ problem_condition_destroy_args destroy)
+{
+ struct problem_condition *cond = xmalloc(sizeof(*cond));
+ cond->field_name = xstrdup(field_name);
+ cond->args = args;
+ cond->evaluate = evaluate;
+ cond->destroy = destroy;
+ return cond;
+}
+
+void
+problem_condition_free(struct problem_condition *cond)
+{
+ if (cond == NULL)
+ return;
+
+ free(cond->field_name);
+
+ if (cond->destroy != NULL)
+ cond->destroy(cond->args);
+
+ free(cond);
+}
+
+/*
* Evaluates a NULL-terminated list of problem conditions as a logical conjunction
*/
-static bool problem_condition_evaluate_and(struct dump_dir *dd,
+static bool problem_condition_evaluate_and(void *data,
+ char *(* getter)(void *, const char *),
+ bool needfree,
const struct problem_condition *const *condition)
{
/* We stop on the first FALSE condition */
while (condition && *condition != NULL)
{
const struct problem_condition *c = *condition;
- char *field_data = dd_load_text(dd, c->field_name);
+ char *field_data = getter(data, c->field_name);
bool value = c->evaluate(field_data, c->args);
- free(field_data);
+ if (needfree)
+ free(field_data);
if (!value)
return false;
++condition;
@@ -42,27 +87,29 @@ static bool problem_condition_evaluate_and(struct dump_dir *dd,
return true;
}
-/*
- * Goes through all problems and selects only problems accessible by caller_uid and
- * problems for which an and_filter gets TRUE
- *
- * @param condition a NULL-terminated list of problem conditions evaluated
- * as conjunction, can be NULL (means always TRUE)
- */
-static GList* scan_directory(const char *path,
- uid_t caller_uid,
- const struct problem_condition *const *condition)
+bool problem_condition_evaluate_dump_dir_and(struct dump_dir *dd,
+ const struct problem_condition *const *condition)
{
- GList *list = NULL;
+ return problem_condition_evaluate_and(dd, (char *(*)(void *, const char*))dd_load_text, /*free*/true, condition);
+}
+
+bool problem_condition_evaluate_problem_data_and(problem_data_t *pd,
+ const struct problem_condition *const *condition)
+{
+ return problem_condition_evaluate_and(pd, (char *(*)(void *, const char*))problem_data_get_content_or_NULL, /*free*/false, condition);
+}
+int foreach_problem_dir_matching(const char *path,
+ uid_t caller_uid,
+ const struct problem_condition *const *condition,
+ dump_dir_callback callback,
+ void *user_data)
+{
DIR *dp = opendir(path);
if (!dp)
- {
- /* We don't want to yell if, say, $XDG_CACHE_DIR/abrt/spool doesn't exist */
- //perror_msg("Can't open directory '%s'", path);
- return list;
- }
+ return 0;
+ int brk = 0;
struct dirent *dent;
while ((dent = readdir(dp)) != NULL)
{
@@ -70,7 +117,7 @@ static GList* scan_directory(const char *path,
continue; /* skip "." and ".." */
char *full_name = concat_path_file(path, dent->d_name);
- if (dump_dir_accessible_by_uid(full_name, caller_uid))
+ if (caller_uid == -1 || dump_dir_accessible_by_uid(full_name, caller_uid))
{
/* Silently ignore *any* errors, not only EACCES.
* We saw "lock file is locked by process PID" error
@@ -85,10 +132,12 @@ static GList* scan_directory(const char *path,
*/
if (dd)
{
- if (!condition || problem_condition_evaluate_and(dd, condition))
+ if (!condition || problem_condition_evaluate_dump_dir_and(dd, condition))
{
- list = g_list_prepend(list, full_name);
- full_name = NULL;
+ ++brk;
+
+ if (callback)
+ callback(dd, user_data);
}
dd_close(dd); //doesn't fail even if dd == NULL
}
@@ -96,6 +145,21 @@ static GList* scan_directory(const char *path,
free(full_name);
}
closedir(dp);
+ return brk;
+}
+
+static void
+prepend_problem_dir_name(struct dump_dir *dd, GList **list)
+{
+ *list = g_list_prepend(*list, xstrdup(dd->dd_dirname));
+}
+
+GList* get_matching_problem_dirs(const char *path,
+ uid_t caller_uid,
+ const struct problem_condition *const *condition)
+{
+ GList *list = NULL;
+ foreach_problem_dir_matching(path, caller_uid, condition, (dump_dir_callback)prepend_problem_dir_name, &list);
/* Why reverse?
* Because N*prepend+reverse is faster than N*append
@@ -114,18 +178,28 @@ struct time_interval
* A problem condition evaluate function for checking of the TIME field against
* an allowed interval
*
- * @param field_data a content from the PID field
+ * @param field_data a content of a field
* @param args a pointer to an instance of struct time_interval
* @return TRUE if a field value is in a specified interval; otherwise FALSE
*/
-static bool time_interval_problem_condition(const char *field_data, const void *args)
+static bool time_interval_problem_condition(const char *field_data, void *args)
{
const struct time_interval *const interval = (const struct time_interval *)args;
- const time_t timestamp = atol(field_data);
+ const time_t timestamp = field_data ? atol(field_data) : 0;
return interval->from <= timestamp && timestamp <= interval->to;
}
+struct problem_condition *
+time_interval_problem_condition_new(const char *field_name, unsigned long from, unsigned long to)
+{
+ struct time_interval *ti = xmalloc(sizeof(*ti));
+ ti->from = from;
+ ti->to = (to == 0 ? time(NULL) : to);
+
+ return problem_condition_new(field_name, ti, time_interval_problem_condition, free);
+}
+
/*
* A problem condition evaluate function passed if strings are equal
*
@@ -133,14 +207,25 @@ static bool time_interval_problem_condition(const char *field_data, const void *
* @param args a checked string
* @return TRUE if both strings are equal; otherwise FALSE
*/
-static bool equal_string_problem_condition(const char *field_data, const void *args)
+static bool equal_string_problem_condition(const char *field_data, void *args)
+{
+ const char *value = (const char *)args;
+
+ return (value == NULL && field_data == NULL)
+ || (value != NULL && field_data != NULL && strcmp(field_data, value) == 0);
+}
+
+struct problem_condition *
+equal_element_value_problem_condition_new(const char *file_name, const char *value)
{
- return !strcmp(field_data, (const char *)args);
+ return problem_condition_new(file_name, (void *)value, equal_string_problem_condition, NULL);
}
+
GList *get_problem_dirs_for_uid(uid_t uid, const char *dump_location)
{
- GList *dirs = scan_directory(dump_location, uid, NULL);
+ /* NULL condition means get all valid and accessible dump dirs */
+ GList *dirs = get_matching_problem_dirs(dump_location, uid, /*condition*/NULL);
return dirs;
}
@@ -154,37 +239,17 @@ GList *get_problem_dirs_for_element_in_time(uid_t uid,
unsigned long timestamp_to,
const char *dump_location)
{
- struct timeval tv;
- /* use the current time if timestamp_to is 0 */
- if (timestamp_to == 0) {
- gettimeofday(&tv, NULL);
- timestamp_to = tv.tv_sec;
- }
+ struct problem_condition *filter[3] = { 0 };
+ filter[0] = time_interval_problem_condition_new(FILENAME_LAST_OCCURRENCE, timestamp_from, timestamp_to);
+
+ if (element)
+ filter[1] = equal_element_value_problem_condition_new(FILENAME_REPORTED_TO, element);
+ GList *dirs = get_matching_problem_dirs(dump_location, uid, (const struct problem_condition *const *)filter);
+
+ problem_condition_free(filter[0]);
+ problem_condition_free(filter[1]);
- const struct problem_condition elementc = {
- .field_name = element,
- .args = value,
- .evaluate = equal_string_problem_condition,
- };
-
- const struct time_interval interval = {
- .from = timestamp_from,
- .to = timestamp_to,
- };
-
- const struct problem_condition timec = {
- .field_name = FILENAME_LAST_OCCURRENCE,
- .args = &interval,
- .evaluate = time_interval_problem_condition
- };
-
- const struct problem_condition *const condition[] = {
- &timec,
- element ? &elementc : NULL,
- NULL
- };
- GList *dirs = scan_directory(dump_location, uid, condition);
return dirs;
}
@@ -203,24 +268,26 @@ GList *get_problem_storages()
typedef struct problem_count_info {
int problem_count;
- unsigned long since;
- unsigned long until;
+ struct problem_condition **filter;
} problem_count_info_t;
static void count_problems_in_dir(char *path, gpointer problem_counter)
{
problem_count_info_t *pci = (problem_count_info_t *)problem_counter;
- VERB2 log("scanning '%s' for problems since %lu", path, pci->since);
- GList *problems = get_problem_dirs_for_element_in_time(getuid(), NULL /*don't filter by element*/, NULL, pci->since, pci->until, path);
- pci->problem_count += g_list_length(problems);
- list_free_with_free(problems);
+ VERB2 log("scanning '%s' for problems matching the filter", path);
+ pci->problem_count += foreach_problem_dir_matching(path, getuid(), (const struct problem_condition * const*)pci->filter,
+ /*callback*/NULL, /*user_data*/NULL);
}
unsigned int get_problems_count(GList *paths, unsigned long since)
{
GList *pths = NULL;
+ struct problem_condition *filter[2];
+ filter[0] = time_interval_problem_condition_new(FILENAME_LAST_OCCURRENCE, since, time(NULL));
+ filter[1] = NULL;
+
if (paths == NULL)
{
pths = get_problem_storages();
@@ -229,12 +296,12 @@ unsigned int get_problems_count(GList *paths, unsigned long since)
problem_count_info_t pci;
pci.problem_count = 0;
- pci.since = since;
- pci.until = 0;
+ pci.filter = filter;
g_list_foreach(paths ? paths : pths, (GFunc)count_problems_in_dir, &pci);
list_free_with_free(pths);
+ problem_condition_free(filter[0]);
return pci.problem_count;
-}
\ No newline at end of file
+}
--
1.8.1.4
More information about the Crash-catcher
mailing list