On 03/12/2013 09:19 AM, Jakub Filak wrote:
- the current implementation stores a set of ignored problems in a
csv
file
- each ignored problem corresponds to a single line in the file
- each line has the following format 'PROBLEM ID;UUID;DUPHASH'
- the matching function returns logical true if any line value equals to
the corresponding problem value
- related to rhbz#909968 and rhbz#905412
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/include/libabrt.h | 56 +++++++++
src/lib/Makefile.am | 3 +-
src/lib/ignored_problems.c | 274 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 332 insertions(+), 1 deletion(-)
create mode 100644 src/lib/ignored_problems.c
diff --git a/src/include/libabrt.h b/src/include/libabrt.h
index 16008b1..75e4d64 100644
--- a/src/include/libabrt.h
+++ b/src/include/libabrt.h
@@ -139,6 +139,62 @@ problem_data_t *get_problem_data_dbus(const char
*problem_dir_path);
*/
GList *get_problems_over_dbus(bool authorize);
+/**
+ @struct ignored_problems
+ @brief An opaque structure holding a list of ignored problems
+*/
+typedef struct ignored_problems ignored_problems_t;
+
+/**
+ @brief Initializes a new instance of ignored problems
+
+ @param file_path A malloced string hodling
s/hodling/holding/
a path to a file containing the list of ignored problems. Function takes ownership of the
malloced memory, whic will be freed in ignored_problems_free()
s/whic /which /
+ @see ignored_problems_free()
+ @return Fully initialized instance of ignored problems struct which must be destroyed
by ignored_problems_free()
+*/
+ignored_problems_t *ignored_problems_new(char *file_path);
+
+/**
+ @brief Destroys an instance of ignored problems
+
+ This function never fails. Supports the common behaviour where it accepts
+ NULL pointers.
+
+ @param set A destroyed instance
+*/
+void ignored_problems_free(ignored_problems_t *set);
+
+/**
+ @brief Adds a problem to the ignored problems
+
+ This function never fails. All errors will be logged.
+
+ @param set An instance of ignored problems to which the problem will be added
+ @param problem_id An identfier of a problem which will be added to an ignored set
s/identfier/identifier/
+*/
+void ignored_problems_add(ignored_problems_t *set, const char *problem_id);
+
+/**
+ @brief Removes a problem from the ignored problems
+
+ This function never fails. All errors will be logged.
+
+ @param set An instance of ignored problems from which the problem will be added
s/added/deleted/
+ @param problem_id An identifier of a problem which will be removed
from an ignored problems struct
+*/
+void ignored_problems_remove(ignored_problems_t *set, const char *problem_id);
+
+/**
+ @brief Checks if a problem is in the ignored problems
+
+ This function never fails. All errors will be logged. If any error occures,
+ the function returns 0 value.
s/occures/occurs/
+
+ @param set An instance of ignored problems in which the problem will be searched
+ @param problem_id An identfier of a problem
identifier
+static bool ignored_problems_eq(ignored_problems_t *set,
+ const char *problem_id, const char *uuid, const char *duphash,
+ char *line, unsigned line_num)
+{
+ char *ignored = line;
+ char *ignored_end = strchr(ignored, IGN_COLUMN_DELIMITER);
+
+ if (ignored_end != NULL)
+ ignored_end[0] = '\0';
+
+ if (strcmp(problem_id, ignored) == 0)
+ {
+ VERB1 log("Ignored id equals to '%s'", problem_id);
+ return true;
+ }
+
+ if (ignored_end == NULL)
+ {
+ VERB1 log("Missing UUID as 2nd column at line %d in ignored problems file
'%s'",
+ line_num, set->ign_set_file_path);
+ return false;
+ }
+
+ ignored_end[0] = IGN_COLUMN_DELIMITER;
I would do this a bit differently:
char *ignored_end = strchrnul(ignored, IGN_COLUMN_DELIMITER);
unsinged sz = ignored_end - ignored;
/* no need to store '\0'! :) */
if (sz == strlen(problem_id) && strncmp(problem_id, ignored, sz) == 0)
{ ...found... }
if (*ignored_end == '\0')
{ ...format error... }
/* no need to restore ';'! :) :) */
+ VERB1 log("Ignored uuid '%s' equals uuid from
of problem '%s'", ignored, problem_id);
"from of problem" ?
+void ignored_problems_add(ignored_problems_t *set, const char
*problem_id)
+{
+ if (ignored_problems_contains(set, problem_id))
+ {
+ VERB1 log("Not adding problem '%s' to ignored problems",
problem_id);
Why not? Let them know:
"Not adding '%s' to ignored problem list - it is already there"
+ return;
+ }
+
+ struct dump_dir *dd = dd_opendir(problem_id, IGN_DD_OPEN_FLAGS);
+
+ if (!dd)
+ {
+ VERB1 log("Can't add the problem '%s' to the ignored problems
because of " \
+ "an error while opening the problem", problem_id);
what error? ENOENT? ENOSPC? EPERM? You don't know:
"an error" means "something went wrong somewhere".
Notice that dd_opendir() is your friend. It emits a better message,
because it knows more details about the failure.
Also, log() isn't for errors. error_msg() is.
I think something like this would be better:
/* dd_opendir() already emitted a message why exactly dd failed to open,
* we add another bit of information:
*/
error_msg("Can't add problem '%s' to the ignored problem list");
+void ignored_problems_remove(ignored_problems_t *set, const char
*problem_id)
+{
+ if (false == ignored_problems_contains(set, problem_id))
+ {
+ VERB1 log("Not removing problem '%s' from ignored problems",
problem_id);
+ return;
+ }
+
+ VERB1 log("Removing problem '%s' from ignored problems",
problem_id);
+
+ char *uuid = NULL;
+ char *duphash = NULL;
+ struct dump_dir *dd = dd_opendir(problem_id, IGN_DD_OPEN_FLAGS);
+
+ if (dd)
+ {
+ uuid = dd_load_text_ext(dd, FILENAME_UUID, IGN_DD_LOAD_TEXT_FLAGS);
+ duphash = dd_load_text_ext(dd, FILENAME_DUPHASH, IGN_DD_LOAD_TEXT_FLAGS);
+ }
+ else
+ {
+ VERB1 log("Can't use neither UUID nor DUPHASH for identification of
" \
You don't need \ at the end of this line.
+ "the problem '%s' to remove it from the
ignored problems " \
+ "becuase of an error while opennig the problem", problem_id);
+ }
+
+ FILE *fp = fopen(set->ign_set_file_path, "r");
+ if (!fp)
+ {
+ VERB1 perror_msg("Can't open the ignored problems '%s' and
determine " \
+ "if the problem '%s' belongs to it",
+ set->ign_set_file_path, problem_id);
+ if (dd)
+ {
+ free(uuid);
+ free(duphash);
+ dd_close(dd);
+ }
+ return;
+ }
+
+ char result_name[] = IGN_TEMP_FILE_TEMPLATE;
+ int result = mkstemp(result_name);
This will create a file in /tmp...
+ full_write(result, line, len + 1);
no error check?
+ if (rename(result_name, set->ign_set_file_path) < 0)
...and here rename() will fail if set->ign_set_file_path is not on the same
filesystem.
I recommend:
char *result_name = xasprintf("%s.XXXXXX", set->ign_set_file_path);
int result = mkstemp(result_name);
...
rename(...)
...
free(result_name);
+ {
+ VERB1 perror_msg("Can't remove remove the problem '%s' from the
ignored " \
"remove remove"?
+ "problems '%s' because an error
while moving an updated " \
+ "file '%s'",
+ problem_id, set->ign_set_file_path, result_name);
+ }
+}
+
+bool ignored_problems_contains(ignored_problems_t *set, const char *problem_id)
+{
+ struct dump_dir *dd = dd_opendir(problem_id, IGN_DD_OPEN_FLAGS);
+
+ if (!dd)
+ {
+ VERB1 log("Can't determine if the problem '%s' belongs to the
ignored " \
+ "problems because of an error while opening the problem",
+ problem_id);
+
+ return false;
+ }
+
+ FILE *fp = fopen(set->ign_set_file_path, "r");
+ if (!fp)
+ {
+ VERB1 perror_msg("Can't open the ignored problems '%s' and
determine " \
+ "if the problem '%s' belongs to it",
+ set->ign_set_file_path, problem_id);
+
+ dd_close(dd);
+ return false;
+ }
I would try to shorten error messages.
It is not that easy without information loss, but...
"Can't open the ignored problems '%s' and determine " \
"if the problem '%s' belongs to it"
|
v
"Can't open '%s'"
would work if filename is expected to be like "ignored_problems.lst".
Else:
"Can't open ignored problem list '%s'"
--
vda