diff options
| author | Karel Klic <kklic@redhat.com> | 2011-03-09 16:49:41 +0100 |
|---|---|---|
| committer | Karel Klic <kklic@redhat.com> | 2011-03-09 16:58:28 +0100 |
| commit | 43d84e3f11e46b7c9a042ff338024dfc41bb4f22 (patch) | |
| tree | 783b1122556f15345290ff8b1623812b6e864827 /src/lib | |
| parent | 6ec12db137f2d0fe18f059fcef2390512d0b2c3f (diff) | |
| parent | c2f2a9f310e7b3bc3725cc8dc7e805fd38c7fbbd (diff) | |
| download | abrt-43d84e3f11e46b7c9a042ff338024dfc41bb4f22.tar.gz abrt-43d84e3f11e46b7c9a042ff338024dfc41bb4f22.tar.xz abrt-43d84e3f11e46b7c9a042ff338024dfc41bb4f22.zip | |
Merge branch 'master' of ssh://git.fedorahosted.org/git/abrt
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/Makefile.am | 23 | ||||
| -rw-r--r-- | src/lib/crash_data.c | 13 | ||||
| -rw-r--r-- | src/lib/event_config.c | 135 | ||||
| -rw-r--r-- | src/lib/event_xml_parser.c | 203 | ||||
| -rw-r--r-- | src/lib/load_plugin_settings.c | 52 | ||||
| -rw-r--r-- | src/lib/run_event.c | 46 | ||||
| -rw-r--r-- | src/lib/spawn.c | 12 | ||||
| -rw-r--r-- | src/lib/xfuncs.c | 14 |
8 files changed, 437 insertions, 61 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index bad3e63a..36fe7b4b 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -43,16 +43,19 @@ libreport_la_SOURCES = \ hooklib.c hooklib.h \ parse_release.c \ parse_options.c parse_options.h \ - steal_directory.c + steal_directory.c \ + event_xml_parser.c \ + event_config.c libreport_la_CPPFLAGS = \ - -Wall -Werror \ + -Wall -Wwrite-strings -Werror \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DVAR_RUN=\"$(VAR_RUN)\" \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ -DCONF_DIR=\"$(CONF_DIR)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ + -DEVENTS_DIR=\"$(EVENTS_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE libreport_la_LDFLAGS = \ @@ -64,14 +67,16 @@ libabrt_dbus_la_SOURCES = \ abrt_dbus.c abrt_dbus.h libabrt_dbus_la_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DVAR_RUN=\"$(VAR_RUN)\" \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ -DCONF_DIR=\"$(CONF_DIR)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ + -DEVENTS_DIR=\"$(EVENTS_DIR)\" \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ - -Wall -Werror \ + -Wall -Wwrite-strings -Werror \ -D_GNU_SOURCE libabrt_dbus_la_LDFLAGS = \ -version-info 0:1:0 @@ -83,13 +88,15 @@ libabrt_web_la_SOURCES = \ abrt_curl.h abrt_curl.c \ abrt_xmlrpc.h abrt_xmlrpc.cpp libabrt_web_la_CPPFLAGS = \ - -Wall -Werror \ + -Wall -Wwrite-strings -Werror \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DVAR_RUN=\"$(VAR_RUN)\" \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ -DCONF_DIR=\"$(CONF_DIR)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ + -DEVENTS_DIR=\"$(EVENTS_DIR)\" \ $(GLIB_CFLAGS) \ $(CURL_CFLAGS) \ $(LIBXML_CFLAGS) \ diff --git a/src/lib/crash_data.c b/src/lib/crash_data.c index 63b0a7a5..7f23c52f 100644 --- a/src/lib/crash_data.c +++ b/src/lib/crash_data.c @@ -233,10 +233,21 @@ void load_crash_data_from_dump_dir(crash_data_t *crash_data, struct dump_dir *dd content = dd_load_text(dd, short_name); } + int flags = 0; + + if (editable) + flags |= CD_FLAG_TXT | CD_FLAG_ISEDITABLE; + else + flags |= CD_FLAG_TXT | CD_FLAG_ISNOTEDITABLE; + + int oneline = strchr(content, '\n') == NULL; + if (oneline) + flags |= CD_FLAG_ONELINE; + add_to_crash_data_ext(crash_data, short_name, content, - (editable ? CD_FLAG_TXT + CD_FLAG_ISEDITABLE : CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE) + flags ); free(short_name); free(full_name); diff --git a/src/lib/event_config.c b/src/lib/event_config.c new file mode 100644 index 00000000..a79ee40c --- /dev/null +++ b/src/lib/event_config.c @@ -0,0 +1,135 @@ +#include "abrtlib.h" + +GHashTable *g_event_config_list; + +event_option_t *new_event_option(void) +{ + return xzalloc(sizeof(event_option_t)); +} + +event_config_t *new_event_config(void) +{ + return xzalloc(sizeof(event_config_t)); +} + +void free_event_option(event_option_t *p) +{ + if (!p) + return; + free(p->name); + free(p->value); + free(p->label); + free(p->description); + free(p->allowed_value); + free(p); +} + +void free_event_config(event_config_t *p) +{ + if (!p) + return; + free(p->name); + free(p->title); + free(p->action); + free(p->description); + for (GList *opt = p->options; opt; opt = opt->next) + free_event_option(opt->data); + g_list_free(p->options); + free(p); +} + + +// (Re)loads data from /etc/abrt/events/*.{conf,xml} +void load_event_config_data(void) +{ + free_event_config_data(); + + DIR *dir = opendir(EVENTS_DIR); + if (!dir) + return; + + if (!g_event_config_list) + g_event_config_list = g_hash_table_new_full( + /*hash_func*/ g_str_hash, + /*key_equal_func:*/ g_str_equal, + /*key_destroy_func:*/ free, + /*value_destroy_func:*/ (GDestroyNotify) free_event_config + ); + + struct dirent *dent; + while ((dent = readdir(dir)) != NULL) + { + char *ext = strrchr(dent->d_name, '.'); + if (!ext) + continue; + bool conf = strcmp(ext + 1, "conf") == 0; + bool xml = strcmp(ext + 1, "xml") == 0; + if (!conf && !xml) + continue; + + char *fullname = concat_path_file(EVENTS_DIR, dent->d_name); + + *ext = '\0'; + event_config_t *event_config = get_event_config(dent->d_name); + bool new_config = (!event_config); + if (new_config) + event_config = new_event_config(); + + if (xml) + load_event_description_from_file(event_config, fullname); + if (conf) + { + map_string_h *keys_and_values = new_map_string(); + + load_conf_file(fullname, keys_and_values, /*skipKeysWithoutValue:*/ false); + + /* Insert or replace every key/value from keys_and_values to event_config->option */ + GHashTableIter iter; + char *name; + char *value; + g_hash_table_iter_init(&iter, keys_and_values); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + event_option_t *opt; + GList *elem = g_list_find(event_config->options, name); + if (elem) + { + opt = elem->data; + free(opt->value); + } + else + { + opt = new_event_option(); + opt->name = xstrdup(name); + } + opt->value = xstrdup(value); + if (!elem) + event_config->options = g_list_append(event_config->options, opt); + } + + free_map_string(keys_and_values); + } + + free(fullname); + + if (new_config) + g_hash_table_replace(g_event_config_list, xstrdup(dent->d_name), event_config); + } +} + +/* Frees all loaded data */ +void free_event_config_data(void) +{ + if (g_event_config_list) + { + g_hash_table_destroy(g_event_config_list); + g_event_config_list = NULL; + } +} + +event_config_t *get_event_config(const char *name) +{ + if (!g_event_config_list) + return NULL; + return g_hash_table_lookup(g_event_config_list, name); +} diff --git a/src/lib/event_xml_parser.c b/src/lib/event_xml_parser.c new file mode 100644 index 00000000..be4a9e09 --- /dev/null +++ b/src/lib/event_xml_parser.c @@ -0,0 +1,203 @@ +#include "abrtlib.h" +#include "event_config.h" + +#define EVENT_ELEMENT "event" +#define LABEL_ELEMENT "label" +#define DESCRIPTION_ELEMENT "description" +#define ALLOW_EMPTY_ELEMENT "allow-empty" +#define OPTION_ELEMENT "option" +#define ACTION_ELEMENT "action" +#define NAME_ELEMENT "name" + +static int in_option = 0; //FIXME + +static const char *const option_types[] = +{ + "text", + "bool", + "password", + "number", + NULL +}; + +// Called for open tags <foo bar="baz"> +static void start_element(GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + //g_print("start: %s\n", element_name); + + event_config_t *ui = user_data; + + if (strcmp(element_name, OPTION_ELEMENT) == 0) + { + if (in_option == 0) + { + in_option = 1; + event_option_t *option = new_event_option(); + //we need to prepend, so ui->options always points to the last created option + VERB2 log("adding option"); + ui->options = g_list_prepend(ui->options, option); + + int i; + for (i = 0; attribute_names[i] != NULL; ++i) + { + VERB2 log("attr: %s:%s", attribute_names[i], attribute_values[i]); + if (strcmp(attribute_names[i], "name") == 0) + { + free(option->name); + option->name = xstrdup(attribute_values[i]); + } + else if (strcmp(attribute_names[i], "type") == 0) + { + option_type_t type; + for (type = OPTION_TYPE_TEXT; type < OPTION_TYPE_INVALID; ++type) + { + if (strcmp(option_types[type], attribute_values[i]) == 0) + option->type = type; + } + } + } + } + else + { + error_msg("error, option nested in option"); + } + } + +} + +// Called for close tags </foo> +static void end_element(GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + event_config_t *ui = user_data; + if (strcmp(element_name, OPTION_ELEMENT) == 0) + { + in_option = 0; + } + if (strcmp(element_name, EVENT_ELEMENT) == 0) + { + //we need to reverse the list, because we we're prepending + ui->options = g_list_reverse(ui->options); + in_option = 0; + } +} + +// Called for character data +// text is not nul-terminated +static void text(GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + event_config_t *ui = user_data; + const gchar * inner_element = g_markup_parse_context_get_element(context); + char *_text = xstrndup(text, text_len); + if (in_option == 1) + { + event_option_t *option = ui->options->data; + if (strcmp(inner_element, LABEL_ELEMENT) == 0) + { + VERB2 log("new label:'%s'", _text); + free(option->label); + option->label = _text; + return; + } + if (strcmp(inner_element, DESCRIPTION_ELEMENT) == 0) + { + VERB2 log("tooltip:'%s'", _text); + free(option->description); + option->description = _text; + return; + } + } + else + { + /* we're not in option, so the description is for the event */ + if (strcmp(inner_element, ACTION_ELEMENT) == 0) + { + VERB2 log("action description:'%s'", _text); + free(ui->action); + ui->action = _text; + return; + } + if (strcmp(inner_element, NAME_ELEMENT) == 0) + { + VERB2 log("event name:'%s'", _text); + free(ui->name); + ui->name = _text; + return; + } + if (strcmp(inner_element, DESCRIPTION_ELEMENT) == 0) + { + VERB2 log("event description:'%s'", _text); + free(ui->description); + ui->description = _text; + return; + } + } + free(_text); +} + + // Called for strings that should be re-saved verbatim in this same + // position, but are not otherwise interpretable. At the moment + // this includes comments and processing instructions. + // text is not nul-terminated +static void passthrough(GMarkupParseContext *context, + const gchar *passthrough_text, + gsize text_len, + gpointer user_data, + GError **error) +{ + VERB2 log("passthrough"); +} + +// Called on error, including one set by other +// methods in the vtable. The GError should not be freed. +static void error(GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + error_msg("error in XML parsing"); +} + +/* this function takes 2 parameters + * ui -> pointer to event_config_t + * filename -> filename to read + * event_config_t contains list of options, which is malloced by hits function + * and must be freed by the caller + */ + +void load_event_description_from_file(event_config_t *event_config, const char* filename) +{ + GMarkupParser parser; + parser.start_element = &start_element; + parser.end_element = &end_element; + parser.text = &text; + parser.passthrough = &passthrough; + parser.error = &error; + GMarkupParseContext *context = g_markup_parse_context_new( + &parser, G_MARKUP_TREAT_CDATA_AS_TEXT, + event_config, /*GDestroyNotify:*/ NULL); + + FILE* fin = fopen(filename, "r"); + if (fin != NULL) + { + size_t read_bytes = 0; + char buff[1024]; + while ((read_bytes = fread(buff, 1, 1024, fin)) != 0) + { + g_markup_parse_context_parse(context, buff, read_bytes, NULL); + } + fclose(fin); + } + + g_markup_parse_context_free(context); +} diff --git a/src/lib/load_plugin_settings.c b/src/lib/load_plugin_settings.c index 1e6b31e7..1b6086f9 100644 --- a/src/lib/load_plugin_settings.c +++ b/src/lib/load_plugin_settings.c @@ -34,47 +34,47 @@ bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWith char *line; while ((line = xmalloc_fgetline(fp)) != NULL) { - unsigned ii; - bool valid = false; bool in_quote = false; /* We are reusing line buffer to form temporary * "key\0value\0..." in its beginning */ - char *key = line; - char *value = line; - char *cur = line; - - for (ii = 0; line[ii] != '\0'; ii++) + char *value = NULL; + char *src; + char *dst; + for (src = dst = line; *src; src++) { - if (line[ii] == '"') + char c = *src; + if (c == '"') { in_quote = !in_quote; } - if (isspace(line[ii]) && !in_quote) - { - continue; - } - if (line[ii] == '#' && !in_quote && cur == line) - { - break; - } - if (line[ii] == '=' && !in_quote) + if (!in_quote) { - valid = true; - *cur++ = '\0'; /* terminate key */ - value = cur; /* remember where value starts */ - continue; + if (isspace(c)) + { + continue; + } + if (c == '#' && dst == line) + { + break; + } + if (c == '=') + { + *dst++ = '\0'; /* terminate key */ + value = dst; /* remember where value starts */ + continue; + } } - *cur++ = line[ii]; /* store next key or value char */ + *dst++ = c; /* store next key or value char */ } - *cur++ = '\0'; /* terminate value */ + *dst = '\0'; /* terminate value */ /* Skip broken or empty lines. */ - if (!valid) + if (!value) goto free_line; /* Skip lines with empty key. */ - if (key[0] == '\0') + if (line[0] == '\0') goto free_line; if (skipKeysWithoutValue && value[0] == '\0') @@ -84,7 +84,7 @@ bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWith if (in_quote) goto free_line; - g_hash_table_replace(settings, xstrdup(key), xstrdup(value)); + g_hash_table_replace(settings, xstrdup(line), xstrdup(value)); free_line: free(line); } diff --git a/src/lib/run_event.c b/src/lib/run_event.c index a2bbc76b..45facffd 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -93,7 +93,7 @@ static GList *load_event_config(GList *list, if (*p == '\0' || *p == '#') goto next_line; /* empty or comment line, skip */ - VERB3 log("%s: line '%s'", __func__, p); + //VERB3 log("%s: line '%s'", __func__, p); if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")])) { @@ -117,15 +117,15 @@ static GList *load_event_config(GList *list, glob_t globbuf; memset(&globbuf, 0, sizeof(globbuf)); - VERB3 log("%s: globbing '%s'", __func__, name_to_glob); + //VERB3 log("%s: globbing '%s'", __func__, name_to_glob); glob(name_to_glob, 0, NULL, &globbuf); free(name_to_glob); char **name = globbuf.gl_pathv; if (name) while (*name) { - VERB3 log("%s: recursing into '%s'", __func__, *name); + //VERB3 log("%s: recursing into '%s'", __func__, *name); list = load_event_config(list, dump_dir_name, event, *name); - VERB3 log("%s: returned from '%s'", __func__, *name); + //VERB3 log("%s: returned from '%s'", __func__, *name); name++; } globfree(&globbuf); @@ -174,10 +174,10 @@ static GList *load_event_config(GList *list, /* Does VAL match? */ if (strcmp(real_val, line_val) != 0) { - VERB3 log("var '%s': '%.*s'!='%s', skipping line", - p, - (int)(strchrnul(real_val, '\n') - real_val), real_val, - line_val); + //VERB3 log("var '%s': '%.*s'!='%s', skipping line", + // p, + // (int)(strchrnul(real_val, '\n') - real_val), real_val, + // line_val); free(malloced_val); goto next_line; /* no */ } @@ -245,18 +245,19 @@ int spawn_next_command(struct run_event_state *state, VERB1 log("Executing '%s'", cmd); /* Export some useful environment variables for children */ + char *env_vec[3]; /* Just exporting dump_dir_name isn't always ok: it can be "." * and some children want to cd to other directory but still * be able to find dump directory by using $DUMP_DIR... */ char *full_name = realpath(dump_dir_name, NULL); - setenv("DUMP_DIR", (full_name ? full_name : dump_dir_name), 1); + env_vec[0] = xasprintf("DUMP_DIR=%s", (full_name ? full_name : dump_dir_name)); free(full_name); - setenv("EVENT", event, 1); -//FIXME: set vars in the child, not here! Need to improve fork_execv_on_steroids... + env_vec[1] = xasprintf("EVENT=%s", event); + env_vec[2] = NULL; char *argv[4]; - argv[0] = (char*)"/bin/sh"; + argv[0] = (char*)"/bin/sh"; // TODO: honor $SHELL? argv[1] = (char*)"-c"; argv[2] = cmd; argv[3] = NULL; @@ -266,12 +267,15 @@ int spawn_next_command(struct run_event_state *state, EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, argv, pipefds, - /* unsetenv_vec: */ NULL, + /* env_vec: */ env_vec, /* dir: */ dump_dir_name, /* uid(unused): */ 0 ); state->command_out_fd = pipefds[0]; + free(env_vec[0]); + free(env_vec[1]); + state->commands = g_list_remove(state->commands, cmd); return 0; @@ -396,7 +400,7 @@ static int list_possible_events_helper(struct strbuf *result, if (*p == '\0' || *p == '#') goto next_line; /* empty or comment line, skip */ - VERB3 log("%s: line '%s'", __func__, p); + //VERB3 log("%s: line '%s'", __func__, p); if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")])) { @@ -420,15 +424,15 @@ static int list_possible_events_helper(struct strbuf *result, glob_t globbuf; memset(&globbuf, 0, sizeof(globbuf)); - VERB3 log("%s: globbing '%s'", __func__, name_to_glob); + //VERB3 log("%s: globbing '%s'", __func__, name_to_glob); glob(name_to_glob, 0, NULL, &globbuf); free(name_to_glob); char **name = globbuf.gl_pathv; if (name) while (*name) { - VERB3 log("%s: recursing into '%s'", __func__, *name); + //VERB3 log("%s: recursing into '%s'", __func__, *name); error = list_possible_events_helper(result, dd, dump_dir_name, pfx, *name); - VERB3 log("%s: returned from '%s'", __func__, *name); + //VERB3 log("%s: returned from '%s'", __func__, *name); if (error) break; name++; @@ -480,10 +484,10 @@ static int list_possible_events_helper(struct strbuf *result, /* Does VAL match? */ if (strcmp(real_val, line_val) != 0) { - VERB3 log("var '%s': '%.*s'!='%s', skipping line", - p, - (int)(strchrnul(real_val, '\n') - real_val), real_val, - line_val); + //VERB3 log("var '%s': '%.*s'!='%s', skipping line", + // p, + // (int)(strchrnul(real_val, '\n') - real_val), real_val, + // line_val); free(real_val); goto next_line; /* no */ } diff --git a/src/lib/spawn.c b/src/lib/spawn.c index f6b7263c..188b63bd 100644 --- a/src/lib/spawn.c +++ b/src/lib/spawn.c @@ -32,7 +32,7 @@ static char *concat_str_vector(char **strings) pid_t fork_execv_on_steroids(int flags, char **argv, int *pipefds, - char **unsetenv_vec, + char **env_vec, const char *dir, uid_t uid) { @@ -69,9 +69,11 @@ pid_t fork_execv_on_steroids(int flags, xsetreuid(uid, uid); } - if (unsetenv_vec) { - while (*unsetenv_vec) - unsetenv(*unsetenv_vec++); + if (env_vec) { + /* Note: we use the glibc extension that putenv("var") + * *unsets* $var if "var" string has no '=' */ + while (*env_vec) + putenv(*env_vec++); } /* Play with stdio descriptors */ @@ -134,7 +136,7 @@ char *run_in_shell_and_save_output(int flags, const char *argv[] = { "/bin/sh", "-c", cmd, NULL }; int pipeout[2]; pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout, - /*unsetenv_vec:*/ NULL, dir, /*uid (unused):*/ 0); + /*env_vec:*/ NULL, dir, /*uid (unused):*/ 0); size_t pos = 0; char *result = NULL; diff --git a/src/lib/xfuncs.c b/src/lib/xfuncs.c index f451693a..3766d231 100644 --- a/src/lib/xfuncs.c +++ b/src/lib/xfuncs.c @@ -215,6 +215,20 @@ void xsetenv(const char *key, const char *value) die_out_of_memory(); } +void safe_unsetenv(const char *var_val) +{ + //char *name = xstrndup(var_val, strchrnul(var_val, '=') - var_val); + //unsetenv(name); + //free(name); + + /* Avoid malloc/free (name is usually very short) */ + unsigned len = strchrnul(var_val, '=') - var_val; + char name[len + 1]; + memcpy(name, var_val, len); + name[len] = '\0'; + unsetenv(name); +} + // Die with an error message if we can't open a new socket. int xsocket(int domain, int type, int protocol) { |
