- so they can be used in abrt-cli and gui
Signed-off-by: Jiri Moskovcak jmoskovc@redhat.com --- po/POTFILES.in | 1 + src/dbus/abrt-dbus.c | 196 +-------------------------------------- src/include/Makefile.am | 3 +- src/include/problem_api.h | 50 ++++++++++ src/lib/Makefile.am | 1 + src/lib/problem_api.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 287 insertions(+), 196 deletions(-) create mode 100644 src/include/problem_api.h create mode 100644 src/lib/problem_api.c
diff --git a/po/POTFILES.in b/po/POTFILES.in index 05fba74..9054e3f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -11,6 +11,7 @@ src/daemon/abrt-handle-event.c src/lib/abrt_conf.c src/lib/hooklib.c src/lib/problem_api.c +src/lib/problem_api_dbus.c src/lib/ignored_problems.c src/plugins/abrt-action-analyze-backtrace.c src/plugins/abrt-action-analyze-c.c diff --git a/src/dbus/abrt-dbus.c b/src/dbus/abrt-dbus.c index 9d7f1ff..4ddf4c3 100644 --- a/src/dbus/abrt-dbus.c +++ b/src/dbus/abrt-dbus.c @@ -7,6 +7,7 @@ #include "abrt-polkit.h" #include "abrt-dbus.h" #include <libreport/dump_dir.h> +#include "problem_api.h"
static GMainLoop *loop; static guint g_timeout_source; @@ -128,201 +129,6 @@ static uid_t get_caller_uid(GDBusConnection *connection, GDBusMethodInvocation * return caller_uid; }
-/* - * 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 *); -}; - -/* - * Evaluates a NULL-terminated list of problem conditions as a logical conjunction - */ -static bool problem_condition_evaluate_and(struct dump_dir *dd, - 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); - bool value = c->evaluate(field_data, c->args); - free(field_data); - if (!value) - return false; - ++condition; - } - - 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) -{ - GList *list = NULL; - - 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; - } - - struct dirent *dent; - while ((dent = readdir(dp)) != NULL) - { - if (dot_or_dotdot(dent->d_name)) - continue; /* skip "." and ".." */ - - char *full_name = concat_path_file(path, dent->d_name); - if (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 - * when we raced with wizard. - */ - int sv_logmode = logmode; - logmode = 0; - struct dump_dir *dd = dd_opendir(full_name, DD_OPEN_READONLY | DD_FAIL_QUIETLY_EACCES); - logmode = sv_logmode; - /* or we could just setuid? - - but it would require locking, because we want to setuid back before we server another request.. - */ - if (dd) - { - if (problem_condition_evaluate_and(dd, condition)) - { - list = g_list_prepend(list, full_name); - full_name = NULL; - } - dd_close(dd); //doesn't fail even if dd == NULL - } - } - free(full_name); - } - closedir(dp); - - /* Why reverse? - * Because N*prepend+reverse is faster than N*append - */ - return g_list_reverse(list); -} - -/* Self explaining time interval structure */ -struct time_interval -{ - unsigned long from; - unsigned long to; -}; - -/* - * 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 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) -{ - const struct time_interval *const interval = (const struct time_interval *)args; - const time_t timestamp = atol(field_data); - - return interval->from <= timestamp && timestamp <= interval->to; -} - -/* - * A problem condition evaluate function passed if strings are equal - * - * @param field_data a content of a field - * @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) -{ - return !strcmp(field_data, (const char *)args); -} - -static GVariant *get_problem_dirs_for_uid(uid_t uid, const char *dump_location) -{ - GList *dirs = scan_directory(dump_location, uid, NULL); - GVariant *result = variant_from_string_list(dirs); - list_free_with_free(dirs); - return result; -} - -/* - * Finds problems with the specified element and which were created in the interval - */ -static GVariant *get_problem_dirs_for_element_in_time(uid_t uid, - const char *element, - const char *value, - unsigned long timestamp_from, - unsigned long timestamp_to, - const char *dump_location) -{ - 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_TIME, - .args = &interval, - .evaluate = time_interval_problem_condition - }; - - const struct problem_condition *const condition[] = { - &elementc, - &timec, - NULL - }; - - GList *dirs = scan_directory(dump_location, uid, condition); - GVariant *result = variant_from_string_list(dirs); - list_free_with_free(dirs); - return result; -} - -static bool allowed_problem_dir(const char *dir_name) -{ -//HACK HACK HACK! Disabled for now until we fix clients (abrt-gui) to not pass /home/user/.cache/abrt/spool -#if 0 - unsigned len = strlen(g_settings_dump_location); - - /* If doesn't start with "g_settings_dump_location[/]"... */ - if (strncmp(dir_name, g_settings_dump_location, len) != 0 - || (dir_name[len] != '/' && dir_name[len] != '\0') - /* or contains "/." anywhere (-> might contain ".." component) */ - || strstr(dir_name + len, "/.") - ) { - return false; - } -#endif - return true; -} - static char *handle_new_problem(GVariant *problem_info, uid_t caller_uid, char **error) { problem_data_t *pd = problem_data_new(); diff --git a/src/include/Makefile.am b/src/include/Makefile.am index ed63afd..addbaa5 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -5,4 +5,5 @@ libabrt_include_HEADERS = \ libabrt.h \ abrt-dbus.h \ hooklib.h \ - satyr-compat.h + satyr-compat.h \ + problem_api.h diff --git a/src/include/problem_api.h b/src/include/problem_api.h new file mode 100644 index 0000000..354b455 --- /dev/null +++ b/src/include/problem_api.h @@ -0,0 +1,50 @@ +/* + Copyright (C) ABRT Team + Copyright (C) RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <glib.h> +#include <libabrt.h> + +/* + * 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 *); +}; + +GVariant *get_problem_dirs_for_uid(uid_t uid, const char *dump_location); +bool allowed_problem_dir(const char *dir_name); +GVariant *get_problem_dirs_for_element_in_time(uid_t uid, + const char *element, + const char *value, + unsigned long timestamp_from, + unsigned long timestamp_to, + const char *dump_location); + +/* Counts all problems in given directories + * + * @paths[in] list of paths to scan (pass NULL to use the default problem directories) + * @returns count of problems + */ +int get_problems_count(const GList *paths); \ No newline at end of file diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 281e5f3..d5c4f5f 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -12,6 +12,7 @@ libabrt_la_SOURCES = \ abrt_glib.c \ migrate_dirs.c \ check_recent_crash_file.c \ + problem_api.c \ problem_api_dbus.c \ ignored_problems.c
diff --git a/src/lib/problem_api.c b/src/lib/problem_api.c new file mode 100644 index 0000000..c6e694b --- /dev/null +++ b/src/lib/problem_api.c @@ -0,0 +1,232 @@ +/* + Copyright (C) ABRT Team + Copyright (C) RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <glib.h> +#include "problem_api.h" + +/* + * Evaluates a NULL-terminated list of problem conditions as a logical conjunction + */ +static bool problem_condition_evaluate_and(struct dump_dir *dd, + 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); + bool value = c->evaluate(field_data, c->args); + free(field_data); + if (!value) + return false; + ++condition; + } + + 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) +{ + GList *list = NULL; + + 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; + } + + struct dirent *dent; + while ((dent = readdir(dp)) != NULL) + { + if (dot_or_dotdot(dent->d_name)) + continue; /* skip "." and ".." */ + + char *full_name = concat_path_file(path, dent->d_name); + if (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 + * when we raced with wizard. + */ + int sv_logmode = logmode; + logmode = 0; + struct dump_dir *dd = dd_opendir(full_name, DD_OPEN_READONLY | DD_FAIL_QUIETLY_EACCES | DD_DONT_WAIT_FOR_LOCK); + logmode = sv_logmode; + /* or we could just setuid? + - but it would require locking, because we want to setuid back before we server another request.. + */ + if (dd) + { + if (!condition || problem_condition_evaluate_and(dd, condition)) + { + list = g_list_prepend(list, full_name); + full_name = NULL; + } + dd_close(dd); //doesn't fail even if dd == NULL + } + } + free(full_name); + } + closedir(dp); + + /* Why reverse? + * Because N*prepend+reverse is faster than N*append + */ + return g_list_reverse(list); +} + +/* Self explaining time interval structure */ +struct time_interval +{ + unsigned long from; + unsigned long to; +}; + +/* + * 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 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) +{ + const struct time_interval *const interval = (const struct time_interval *)args; + const time_t timestamp = atol(field_data); + + return interval->from <= timestamp && timestamp <= interval->to; +} + +/* + * A problem condition evaluate function passed if strings are equal + * + * @param field_data a content of a field + * @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) +{ + return !strcmp(field_data, (const char *)args); +} + +GVariant *get_problem_dirs_for_uid(uid_t uid, const char *dump_location) +{ + GList *dirs = scan_directory(dump_location, uid, NULL); + GVariant *result = variant_from_string_list(dirs); + list_free_with_free(dirs); + return result; +} + +bool allowed_problem_dir(const char *dir_name) +{ +//HACK HACK HACK! Disabled for now until we fix clients (abrt-gui) to not pass /home/user/.cache/abrt/spool +#if 0 + unsigned len = strlen(g_settings_dump_location); + + /* If doesn't start with "g_settings_dump_location[/]"... */ + if (strncmp(dir_name, g_settings_dump_location, len) != 0 + || (dir_name[len] != '/' && dir_name[len] != '\0') + /* or contains "/." anywhere (-> might contain ".." component) */ + || strstr(dir_name + len, "/.") + ) { + return false; + } +#endif + return true; +} + +/* + * Finds problems which were created in the interval + */ +GVariant *get_problem_dirs_for_element_in_time(uid_t uid, + const char *element, + const char *value, + unsigned long timestamp_from, + unsigned long timestamp_to, + const char *dump_location) +{ + 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_TIME, + .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); + GVariant *result = variant_from_string_list(dirs); + list_free_with_free(dirs); + return result; +} + + +static void count_problems_in_dir(char *path, gpointer problem_counter) +{ + VERB2 log("scanning '%s'", path); + int *count = (int *)problem_counter; + GList *problems = scan_directory(path, getuid(), NULL); + *count += g_list_length(problems); + list_free_with_free(problems); +} + +int get_problems_count(const GList *paths) +{ + GList *pths = NULL; + + if (!paths) + { + load_abrt_conf(); + pths = g_list_append(pths, xstrdup(g_settings_dump_location)); + //no needed, we don't steal directories anymore + pths = g_list_append(pths, concat_path_file(g_get_user_cache_dir(), "abrt/spool")); + free_abrt_conf_data(); + } + + int problem_count = 0; + g_list_foreach(pths, (GFunc)count_problems_in_dir, &problem_count); + + return problem_count; +} \ No newline at end of file