[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