diff options
author | Karel Klic <kklic@redhat.com> | 2011-03-01 12:08:36 +0100 |
---|---|---|
committer | Karel Klic <kklic@redhat.com> | 2011-03-01 12:08:36 +0100 |
commit | 85f639b7fe277ba327e5013e5b101b4a67f14e1d (patch) | |
tree | 7caa3999e8c987e3ddbc26f4bfbbdc73defca73f /src/lib | |
parent | fb52104af74bbf6eeda394880666df40b4354aba (diff) | |
parent | 77468fcdd7cc05db52320c373a24a5490ff32f52 (diff) | |
download | abrt-85f639b7fe277ba327e5013e5b101b4a67f14e1d.tar.gz abrt-85f639b7fe277ba327e5013e5b101b4a67f14e1d.tar.xz abrt-85f639b7fe277ba327e5013e5b101b4a67f14e1d.zip |
merge changes from master
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/CommLayerInner.cpp | 94 | ||||
-rw-r--r-- | src/lib/Makefile.am | 66 | ||||
-rw-r--r-- | src/lib/abrt_curl.c | 15 | ||||
-rw-r--r-- | src/lib/abrt_dbus.c | 438 | ||||
-rw-r--r-- | src/lib/abrt_dbus.h | 59 | ||||
-rw-r--r-- | src/lib/abrt_types.c (renamed from src/lib/Plugin.cpp) | 36 | ||||
-rw-r--r-- | src/lib/abrt_xmlrpc.cpp | 18 | ||||
-rw-r--r-- | src/lib/abrt_xmlrpc.h | 12 | ||||
-rw-r--r-- | src/lib/binhex.c | 74 | ||||
-rw-r--r-- | src/lib/copy_file_recursive.c | 139 | ||||
-rw-r--r-- | src/lib/crash_data.c (renamed from src/lib/crash_dump.cpp) | 187 | ||||
-rw-r--r-- | src/lib/create_dump_dir.c | 85 | ||||
-rw-r--r-- | src/lib/dump_dir.c | 468 | ||||
-rw-r--r-- | src/lib/glib_support.c (renamed from src/lib/ABRTException.cpp) | 21 | ||||
-rw-r--r-- | src/lib/hash_md5.h | 3 | ||||
-rw-r--r-- | src/lib/hash_sha1.h | 3 | ||||
-rw-r--r-- | src/lib/hooklib.c | 2 | ||||
-rw-r--r-- | src/lib/hooklib.h | 8 | ||||
-rw-r--r-- | src/lib/iso_date_string.c (renamed from src/lib/numtoa.cpp) | 25 | ||||
-rw-r--r-- | src/lib/load_plugin_settings.c (renamed from src/lib/load_plugin_settings.cpp) | 38 | ||||
-rw-r--r-- | src/lib/logging.h | 29 | ||||
-rw-r--r-- | src/lib/make_descr.c (renamed from src/lib/make_descr.cpp) | 136 | ||||
-rw-r--r-- | src/lib/parse_options.c | 45 | ||||
-rw-r--r-- | src/lib/parse_options.h | 32 | ||||
-rw-r--r-- | src/lib/parse_release.c (renamed from src/lib/parse_release.cpp) | 32 | ||||
-rw-r--r-- | src/lib/read_write.h | 6 | ||||
-rw-r--r-- | src/lib/run_event.c | 414 | ||||
-rw-r--r-- | src/lib/spawn.c | 2 | ||||
-rw-r--r-- | src/lib/steal_directory.c | 40 | ||||
-rw-r--r-- | src/lib/strbuf.c | 2 | ||||
-rw-r--r-- | src/lib/strbuf.h | 11 | ||||
-rw-r--r-- | src/lib/stringops.cpp | 57 | ||||
-rw-r--r-- | src/lib/xatonum.c | 4 | ||||
-rw-r--r-- | src/lib/xfuncs.c | 22 |
34 files changed, 1933 insertions, 690 deletions
diff --git a/src/lib/CommLayerInner.cpp b/src/lib/CommLayerInner.cpp deleted file mode 100644 index 3c102d6e..00000000 --- a/src/lib/CommLayerInner.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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 <pthread.h> -#include <map> -#include "abrtlib.h" -#include "comm_layer_inner.h" - -static CObserver *s_pObs; - -typedef std::map<uint64_t, std::string> map_uint_str_t; -static map_uint_str_t s_mapClientID; -static pthread_mutex_t s_map_mutex; -static bool s_map_mutex_inited; - -/* called via [p]error_msg() */ -static void warn_client(const char *msg) -{ - if (!s_pObs) - return; - - uint64_t key = uint64_t(pthread_self()); - - pthread_mutex_lock(&s_map_mutex); - map_uint_str_t::const_iterator ki = s_mapClientID.find(key); - const char* peer = (ki != s_mapClientID.end() ? ki->second.c_str() : NULL); - pthread_mutex_unlock(&s_map_mutex); - - if (peer) - s_pObs->Warning(msg, peer); -} - -void init_daemon_logging(CObserver *pObs) -{ - s_pObs = pObs; - if (!s_map_mutex_inited) - { - s_map_mutex_inited = true; - pthread_mutex_init(&s_map_mutex, NULL); - g_custom_logger = &warn_client; - } -} - -void set_client_name(const char *name) -{ - uint64_t key = uint64_t(pthread_self()); - - pthread_mutex_lock(&s_map_mutex); - if (!name) { - s_mapClientID.erase(key); - } else { - s_mapClientID[key] = name; - } - pthread_mutex_unlock(&s_map_mutex); -} - -void update_client(const char *fmt, ...) -{ - if (!s_pObs) - return; - - uint64_t key = uint64_t(pthread_self()); - - pthread_mutex_lock(&s_map_mutex); - map_uint_str_t::const_iterator ki = s_mapClientID.find(key); - const char* peer = (ki != s_mapClientID.end() ? ki->second.c_str() : NULL); - pthread_mutex_unlock(&s_map_mutex); - - if (!peer) - return; - - va_list p; - va_start(p, fmt); - char *msg = xvasprintf(fmt, p); - va_end(p); - - s_pObs->Status(msg, peer); - free(msg); -} diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ad2d2025..bad3e63a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,69 +1,69 @@ -# libabrt - the stuff shared among most of abrt (like xmalloc, logging) -# libabrt_daemon - only daemon related things are here +# libreport - the stuff shared among most of abrt (like xmalloc, logging) # libabrt_dbus - daemon, cli and applet use this # libabrt_web - for abrt-action-foo where foo deals with network/web/ftp/... lib_LTLIBRARIES = \ - libabrt.la \ - libabrt_daemon.la \ + libreport.la \ libabrt_dbus.la \ libabrt_web.la -HEADER_DIR = $(srcdir)/../include -AM_CPPFLAGS = -I$(HEADER_DIR) - # Not used just yet: # time.cpp # xconnect.cpp -libabrt_la_SOURCES = \ +libreport_la_SOURCES = \ xfuncs.c \ encbase64.c \ + binhex.c \ stdio_helpers.c \ hash_md5.c hash_md5.h \ hash_sha1.c hash_sha1.h \ read_write.c read_write.h \ logging.c logging.h \ copyfd.c \ + copy_file_recursive.c \ concat_path_file.c \ append_to_malloced_string.c \ overlapping_strcpy.c \ skip_whitespace.c \ - stringops.cpp \ + glib_support.c \ + iso_date_string.c \ strbuf.c strbuf.h \ - xatonum.c numtoa.cpp \ + xatonum.c \ spawn.c \ dirsize.c \ dump_dir.c \ get_cmdline.c \ daemon_is_ok.c \ - load_plugin_settings.cpp \ - make_descr.cpp \ + load_plugin_settings.c \ + make_descr.c \ run_event.c \ - crash_dump.cpp \ - ABRTException.cpp \ + crash_data.c \ + create_dump_dir.c \ + abrt_types.c \ hooklib.c hooklib.h \ - parse_release.cpp \ - parse_options.c parse_options.h -libabrt_la_CPPFLAGS = \ + parse_release.c \ + parse_options.c parse_options.h \ + steal_directory.c +libreport_la_CPPFLAGS = \ -Wall -Werror \ - -I$(srcdir)/../include \ + -I$(srcdir)/../include/report -I$(srcdir)/../include \ -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)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE -libabrt_la_LDFLAGS = \ +libreport_la_LDFLAGS = \ -version-info 0:1:0 -libabrt_la_LIBADD = \ +libreport_la_LIBADD = \ $(GLIB_LIBS) libabrt_dbus_la_SOURCES = \ abrt_dbus.c abrt_dbus.h libabrt_dbus_la_CPPFLAGS = \ - -Wall -Werror \ - -I$(srcdir)/../include \ + -I$(srcdir)/../include/report -I$(srcdir)/../include \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ @@ -71,6 +71,7 @@ libabrt_dbus_la_CPPFLAGS = \ -DVAR_RUN=\"$(VAR_RUN)\" \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ + -Wall -Werror \ -D_GNU_SOURCE libabrt_dbus_la_LDFLAGS = \ -version-info 0:1:0 @@ -78,34 +79,18 @@ libabrt_dbus_la_LIBADD = \ $(GLIB_LIBS) \ $(DBUS_LIBS) -libabrt_daemon_la_SOURCES = \ - $(HEADER_DIR)/comm_layer_inner.h CommLayerInner.cpp \ - $(HEADER_DIR)/plugin.h Plugin.cpp -libabrt_daemon_la_CPPFLAGS = \ - -Wall \ - -I$(srcdir)/../include \ - -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)\" \ - -D_GNU_SOURCE -libabrt_daemon_la_LDFLAGS = \ - -version-info 0:1:0 -libabrt_daemon_la_LIBADD = \ - -ldl - libabrt_web_la_SOURCES = \ abrt_curl.h abrt_curl.c \ abrt_xmlrpc.h abrt_xmlrpc.cpp libabrt_web_la_CPPFLAGS = \ -Wall -Werror \ - -I$(srcdir)/../include \ + -I$(srcdir)/../include/report -I$(srcdir)/../include \ -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)\" \ + $(GLIB_CFLAGS) \ $(CURL_CFLAGS) \ $(LIBXML_CFLAGS) \ $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \ @@ -113,6 +98,7 @@ libabrt_web_la_CPPFLAGS = \ libabrt_web_la_LDFLAGS = \ -version-info 0:1:0 libabrt_web_la_LIBADD = \ + $(GLIB_LIBS) \ $(CURL_LIBS) \ $(LIBXML_LIBS) \ $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) diff --git a/src/lib/abrt_curl.c b/src/lib/abrt_curl.c index f23d3949..a17f14d2 100644 --- a/src/lib/abrt_curl.c +++ b/src/lib/abrt_curl.c @@ -287,8 +287,10 @@ abrt_post(abrt_post_state_t *state, // truncates data_size on 32-bit arch. Need xcurl_easy_setopt_long_long()? // Also, I'm not sure CURLOPT_POSTFIELDSIZE_LARGE special-cases -1. } - // Override "Content-Type:" + struct curl_slist *httpheader_list = NULL; + + // Override "Content-Type:" if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) { char *content_type_header = xasprintf("Content-Type: %s", content_type); @@ -300,6 +302,12 @@ abrt_post(abrt_post_state_t *state, xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list); } + // Override "Accept: text/plain": helps convince server to send plain-text + // error messages in the body of HTTP error responses [not verified to work] + httpheader_list = curl_slist_append(httpheader_list, "Accept: text/plain"); + if (!httpheader_list) + error_msg_and_die("out of memory"); + // Disabled: was observed to also handle "305 Use proxy" redirect, // apparently with POST->GET remapping - which server didn't like at all. // Attempted to suppress remapping on 305 using CURLOPT_POSTREDIR of -1, @@ -354,6 +362,11 @@ abrt_post(abrt_post_state_t *state, goto ret; } + // curl-7.20.1 doesn't do it, we get NULL body in the log message below + // unless we fflush the body memstream ourself + if (body_stream) + fflush(body_stream); + // Headers/body are already saved (if requested), extract more info curl_err = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &response_code); die_if_curl_error(curl_err); diff --git a/src/lib/abrt_dbus.c b/src/lib/abrt_dbus.c index 6bc155e3..5d8d861d 100644 --- a/src/lib/abrt_dbus.c +++ b/src/lib/abrt_dbus.c @@ -17,7 +17,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include <dbus/dbus.h> -#include <glib.h> #include "abrtlib.h" #include "abrt_dbus.h" @@ -156,6 +155,98 @@ void store_string(DBusMessageIter* iter, const char* val) free((char*)sanitized); } +/* Helper for storing map_string */ +void store_map_string(DBusMessageIter* dbus_iter, map_string_h *val) +{ + DBusMessageIter sub_iter; + /* map_string is a map. map in dbus is an array of two element structs "({...})": + * "s" (string) for key and "s" for value (in this case, also string) */ + if (!dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY, "{ss}", &sub_iter)) + die_out_of_memory(); + + GHashTableIter iter; + char *name; + char *value; + g_hash_table_iter_init(&iter, val); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + DBusMessageIter sub_sub_iter; + if (!dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_sub_iter)) + die_out_of_memory(); + store_string(&sub_sub_iter, name); + store_string(&sub_sub_iter, value); + if (!dbus_message_iter_close_container(&sub_iter, &sub_sub_iter)) + die_out_of_memory(); + } + + if (!dbus_message_iter_close_container(dbus_iter, &sub_iter)) + die_out_of_memory(); +} + +/* Helpers for storing crash_data */ + +static void store_crash_item(DBusMessageIter* iter, struct crash_item *val) +{ + DBusMessageIter sub_iter; + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub_iter)) + die_out_of_memory(); + + /* Compat with python/cli: + * Crash item is represented in dbus as 3-element vector of strings: + * type, editable, content. + * This doesn't match daemon-side representation: { content, flags } struct + */ + store_string(&sub_iter, (val->flags & CD_FLAG_BIN ? "b" : "t")); + store_string(&sub_iter, (val->flags & CD_FLAG_ISEDITABLE ? "y" : "n")); + store_string(&sub_iter, val->content); + + if (!dbus_message_iter_close_container(iter, &sub_iter)) + die_out_of_memory(); +} +void store_crash_data(DBusMessageIter* dbus_iter, crash_data_t *val) +{ + DBusMessageIter sub_iter; + /* crash_data is a map. map in dbus is an array of two element structs "({...})": + * "s" (string) for key and "as" for value (in this case, array of strings) */ + if (!dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY, "{sas}", &sub_iter)) + die_out_of_memory(); + + GHashTableIter iter; + char *name; + struct crash_item *value; + g_hash_table_iter_init(&iter, val); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + DBusMessageIter sub_sub_iter; + if (!dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_sub_iter)) + die_out_of_memory(); + store_string(&sub_sub_iter, name); + store_crash_item(&sub_sub_iter, value); + if (!dbus_message_iter_close_container(&sub_iter, &sub_sub_iter)) + die_out_of_memory(); + } + + if (!dbus_message_iter_close_container(dbus_iter, &sub_iter)) + die_out_of_memory(); +} +void store_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t *val) +{ + DBusMessageIter sub_iter; + /* "array of maps". map in dbus is an array ("a") of two element structs "({...})": + * "s" (string) for key and "as" for value (in this case, array of strings) */ + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "a{sas}", &sub_iter)) + die_out_of_memory(); + + for (unsigned i = 0; i < val->len; i++) + { + crash_data_t *crash_data = get_crash_data(val, i); + store_crash_data(&sub_iter, crash_data); + } + + if (!dbus_message_iter_close_container(iter, &sub_iter)) + die_out_of_memory(); +} + /* * Helpers for parsing DBus messages @@ -217,6 +308,8 @@ int load_uint64(DBusMessageIter* iter, uint64_t *val) } int load_charp(DBusMessageIter* iter, const char** val) { + *val = NULL; + int type = dbus_message_iter_get_arg_type(iter); if (type != DBUS_TYPE_STRING) { @@ -228,6 +321,157 @@ int load_charp(DBusMessageIter* iter, const char** val) return dbus_message_iter_next(iter); } +/* Helpers for loading crash_data */ + +static int load_crash_item(DBusMessageIter* iter, struct crash_item *item) +{ + int type = dbus_message_iter_get_arg_type(iter); + if (type != DBUS_TYPE_ARRAY) + { + error_msg("array expected in dbus message, but not found ('%c')", type); + return -1; + } + + /* Compat with python/cli: + * Crash item is represented in dbus as 3-element vector of strings: + * type, editable, content. + * This doesn't match daemon-side representation: { content, flags } struct + */ + + DBusMessageIter sub_iter; + dbus_message_iter_recurse(iter, &sub_iter); + + const char *typestr; + int r = load_charp(&sub_iter, &typestr); + if (r != ABRT_DBUS_MORE_FIELDS) + { + error_msg("malformed crash_item element in dbus message"); + return -1; + } + const char *editable; + r = load_charp(&sub_iter, &editable); + if (r != ABRT_DBUS_MORE_FIELDS) + { + error_msg("malformed crash_item element in dbus message"); + return -1; + } + const char *content; + r = load_charp(&sub_iter, &content); + if (r != ABRT_DBUS_LAST_FIELD) + { + error_msg("malformed crash_item element in dbus message"); + return -1; + } + item->flags = 0; + if (typestr[0] == 'b') item->flags |= CD_FLAG_BIN; + if (typestr[0] == 't') item->flags |= CD_FLAG_TXT; + if (editable[0] == 'y') item->flags |= CD_FLAG_ISEDITABLE; + if (editable[0] == 'n') item->flags |= CD_FLAG_ISNOTEDITABLE; + item->content = xstrdup(content); + return 0; +} +int load_crash_data(DBusMessageIter* iter, crash_data_t **val) +{ + *val = NULL; + + int type = dbus_message_iter_get_arg_type(iter); + if (type != DBUS_TYPE_ARRAY) + { + error_msg("array expected in dbus message, but not found ('%c')", type); + return -1; + } + + crash_data_t *result = new_crash_data(); + + DBusMessageIter sub_iter; + dbus_message_iter_recurse(iter, &sub_iter); + + bool next_exists; + int r; +//int cnt = 0; + do { + type = dbus_message_iter_get_arg_type(&sub_iter); + if (type != DBUS_TYPE_DICT_ENTRY) + { + /* When the map has 0 elements, we see DBUS_TYPE_INVALID (on the first iteration) */ + if (type == DBUS_TYPE_INVALID) + break; + error_msg("sub_iter type is not DBUS_TYPE_DICT_ENTRY (%c)!", type); + free_crash_data(result); + return -1; + } + + DBusMessageIter sub_sub_iter; + dbus_message_iter_recurse(&sub_iter, &sub_sub_iter); + + const char *key; + r = load_charp(&sub_sub_iter, &key); + if (r != ABRT_DBUS_MORE_FIELDS) + { + if (r == ABRT_DBUS_LAST_FIELD) + error_msg("malformed map element in dbus message"); + free_crash_data(result); + return -1; + } + struct crash_item *value = xzalloc(sizeof(*value)); + r = load_crash_item(&sub_sub_iter, value); + if (r != ABRT_DBUS_LAST_FIELD) + { + if (r == ABRT_DBUS_MORE_FIELDS) + error_msg("malformed map element in dbus message"); + free(value); + free_crash_data(result); + return -1; + } + g_hash_table_replace(result, xstrdup(key), value); +//cnt++; + next_exists = dbus_message_iter_next(&sub_iter); + } while (next_exists); +//log("%s: %d elems", __func__, cnt); + + *val = result; + return dbus_message_iter_next(iter); /* note: this can't fail (returns bool, thus never < 0) */ +} +int load_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t **val) +{ + *val = NULL; + + int type = dbus_message_iter_get_arg_type(iter); + if (type != DBUS_TYPE_ARRAY) + { + error_msg("array expected in dbus message, but not found ('%c')", type); + return -1; + } + + DBusMessageIter sub_iter; + dbus_message_iter_recurse(iter, &sub_iter); + + vector_of_crash_data_t *result = new_vector_of_crash_data(); + + int r; +//int cnt = 0; + /* When the vector has 0 elements, we see DBUS_TYPE_INVALID here */ + type = dbus_message_iter_get_arg_type(&sub_iter); + if (type != DBUS_TYPE_INVALID) + { + do { + crash_data_t *cd = NULL; +//cnt++; + r = load_crash_data(&sub_iter, &cd); + if (r < 0) + { + free_vector_of_crash_data(result); + return r; + } + g_ptr_array_add(result, cd); + } while (r == ABRT_DBUS_MORE_FIELDS); + } +//log("%s: %d elems", __func__, cnt); + + *val = result; + return dbus_message_iter_next(iter); /* note: this can't fail (returns bool, thus never < 0) */ +} + /* * Glib integration machinery @@ -353,8 +597,29 @@ static void unregister_vtable(DBusConnection *conn, void* data) VERB3 log("%s()", __func__); } + /* - * Initialization works as follows: + * Simple logging handler for dbus errors. + */ +int log_dbus_error(const char *msg, DBusError *err) +{ + int ret = 0; + if (dbus_error_is_set(err)) + { + error_msg("dbus error: %s", err->message); + ret = 1; + } + if (msg) + { + error_msg(msg); + ret = 1; + } + return ret; +} + + +/* + * Initialization. Works as follows: * * we have a DBusConnection* (say, obtained with dbus_bus_get) * we call dbus_connection_set_watch_functions @@ -428,3 +693,172 @@ void attach_dbus_conn_to_glib_main_loop(DBusConnection* conn, } } } + + +/* + * Support functions for clients + */ + +/* helpers */ +static DBusMessage* new_call_msg(const char* method) +{ + DBusMessage* msg = dbus_message_new_method_call(ABRTD_DBUS_NAME, ABRTD_DBUS_PATH, ABRTD_DBUS_IFACE, method); + if (!msg) + die_out_of_memory(); + return msg; +} + +static DBusMessage* send_get_reply_and_unref(DBusMessage* msg) +{ + dbus_uint32_t serial; + if (TRUE != dbus_connection_send(g_dbus_conn, msg, &serial)) + error_msg_and_die("Error sending DBus message"); + dbus_message_unref(msg); + + while (true) + { + DBusMessage *received = dbus_connection_pop_message(g_dbus_conn); + if (!received) + { + if (FALSE == dbus_connection_read_write(g_dbus_conn, -1)) + error_msg_and_die("dbus connection closed"); + continue; + } + + int tp = dbus_message_get_type(received); + const char *error_str = dbus_message_get_error_name(received); +#if 0 + /* Debugging */ + printf("type:%u (CALL:%u, RETURN:%u, ERROR:%u, SIGNAL:%u)\n", tp, + DBUS_MESSAGE_TYPE_METHOD_CALL, + DBUS_MESSAGE_TYPE_METHOD_RETURN, + DBUS_MESSAGE_TYPE_ERROR, + DBUS_MESSAGE_TYPE_SIGNAL + ); + const char *sender = dbus_message_get_sender(received); + if (sender) + printf("sender: %s\n", sender); + const char *path = dbus_message_get_path(received); + if (path) + printf("path: %s\n", path); + const char *member = dbus_message_get_member(received); + if (member) + printf("member: %s\n", member); + const char *interface = dbus_message_get_interface(received); + if (interface) + printf("interface: %s\n", interface); + const char *destination = dbus_message_get_destination(received); + if (destination) + printf("destination: %s\n", destination); + if (error_str) + printf("error: '%s'\n", error_str); +#endif + + DBusError err; + dbus_error_init(&err); + + if (dbus_message_is_signal(received, ABRTD_DBUS_IFACE, "Update")) + { + const char *update_msg; + if (!dbus_message_get_args(received, &err, + DBUS_TYPE_STRING, &update_msg, + DBUS_TYPE_INVALID)) + { + error_msg_and_die("dbus Update message: arguments mismatch"); + } + printf(">> %s\n", update_msg); + } + else if (dbus_message_is_signal(received, ABRTD_DBUS_IFACE, "Warning")) + { + const char *warning_msg; + if (!dbus_message_get_args(received, &err, + DBUS_TYPE_STRING, &warning_msg, + DBUS_TYPE_INVALID)) + { + error_msg_and_die("dbus Warning message: arguments mismatch"); + } + log(">! %s\n", warning_msg); + } + else + if (tp == DBUS_MESSAGE_TYPE_METHOD_RETURN + && dbus_message_get_reply_serial(received) == serial + ) { + return received; + } + else + if (tp == DBUS_MESSAGE_TYPE_ERROR + && dbus_message_get_reply_serial(received) == serial + ) { + error_msg_and_die("dbus call returned error: '%s'", error_str); + } + + dbus_message_unref(received); + } +} + +int32_t call_DeleteDebugDump(const char *dump_dir_name) +{ + DBusMessage* msg = new_call_msg(__func__ + 5); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &dump_dir_name, + DBUS_TYPE_INVALID); + + DBusMessage *reply = send_get_reply_and_unref(msg); + + DBusMessageIter in_iter; + dbus_message_iter_init(reply, &in_iter); + + int32_t result; + int r = load_int32(&in_iter, &result); + if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ + error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5); + + dbus_message_unref(reply); + return result; +} + +static int connect_to_abrtd_and_call_DeleteDebugDump(const char *dump_dir_name) +{ + DBusError err; + dbus_error_init(&err); + g_dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (log_dbus_error( + g_dbus_conn ? NULL : + "error requesting system DBus, possible reasons: " + "dbus config is incorrect; dbus-daemon is not running, " + "or dbus daemon needs to be restarted to reload dbus config", + &err + ) + ) { + if (g_dbus_conn) + dbus_connection_unref(g_dbus_conn); + g_dbus_conn = NULL; + return 1; + } + + int ret = call_DeleteDebugDump(dump_dir_name); + if (ret == ENOENT) + error_msg("Dump directory '%s' is not found", dump_dir_name); + else if (ret != 0) + error_msg("Can't delete dump directory '%s'", dump_dir_name); + + dbus_connection_unref(g_dbus_conn); + g_dbus_conn = NULL; + + return ret; +} + +int delete_dump_dir_possibly_using_abrtd(const char *dump_dir_name) +{ + /* Try to delete it ourselves */ + struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); + if (dd) + { + if (dd->locked) /* it is not readonly */ + return dd_delete(dd); + dd_close(dd); + } + + VERB1 log("Deleting '%s' via abrtd dbus call", dump_dir_name); + return connect_to_abrtd_and_call_DeleteDebugDump(dump_dir_name); +} diff --git a/src/lib/abrt_dbus.h b/src/lib/abrt_dbus.h index cdc963ca..8f559980 100644 --- a/src/lib/abrt_dbus.h +++ b/src/lib/abrt_dbus.h @@ -23,6 +23,11 @@ #include "abrtlib.h" +#define ABRTD_DBUS_NAME "com.redhat.abrt" +#define ABRTD_DBUS_PATH "/com/redhat/abrt" +#define ABRTD_DBUS_IFACE "com.redhat.abrt" + + #ifdef __cplusplus extern "C" { #endif @@ -53,6 +58,7 @@ extern DBusConnection* g_dbus_conn; * // (note: "iface.sig.emitted.from" is not optional for signals!) * dbus_message_set_destination(msg, "peer"); // optional * dbus_connection_send(conn, msg, &serial); // &serial can be NULL + * dbus_connection_unref(conn); // if you don't want to *stay* connected * * - client which receives and processes signals: * conn = dbus_bus_get(DBUS_BUS_SYSTEM/SESSION, &err); @@ -73,6 +79,19 @@ void attach_dbus_conn_to_glib_main_loop(DBusConnection* conn, DBusHandlerResult (*message_received_func)(DBusConnection *conn, DBusMessage *msg, void* data) ); +/* Log dbus error if err has it set. Then log msg if it's !NULL. + * In both cases return 1. Otherwise return 0. + */ +int log_dbus_error(const char *msg, DBusError *err); + +/* Perform "DeleteDebugDump" call over g_dbus_conn */ +int32_t call_DeleteDebugDump(const char *dump_dir_name); + +/* Connect to system bus, find abrtd, perform "DeleteDebugDump" call, close g_dbus_conn */ +/* now static: int connect_to_abrtd_and_call_DeleteDebugDump(const char *dump_dir_name); */ +int delete_dump_dir_possibly_using_abrtd(const char *dump_dir_name); + + /* * Helpers for building DBus messages */ @@ -82,6 +101,9 @@ void store_uint32(DBusMessageIter* iter, uint32_t val); void store_int64(DBusMessageIter* iter, int64_t val); void store_uint64(DBusMessageIter* iter, uint64_t val); void store_string(DBusMessageIter* iter, const char* val); +void store_crash_data(DBusMessageIter* iter, crash_data_t *val); +void store_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t *val); +void store_map_string(DBusMessageIter* iter, map_string_h *val); /* * Helpers for parsing DBus messages @@ -103,13 +125,18 @@ int load_uint32(DBusMessageIter* iter, uint32_t *val); int load_int64(DBusMessageIter* iter, int64_t *val); int load_uint64(DBusMessageIter* iter, uint64_t *val); int load_charp(DBusMessageIter* iter, const char **val); +int load_crash_data(DBusMessageIter* iter, crash_data_t **val); +int load_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t **val); #ifdef __cplusplus } #endif -/* C++ style stuff */ +/* + * C++ style stuff + */ + #ifdef __cplusplus #include <map> @@ -119,6 +146,20 @@ int load_charp(DBusMessageIter* iter, const char **val); * Helpers for building DBus messages */ +static inline std::string ssprintf(const char *format, ...) +{ + va_list p; + char *string_ptr; + + va_start(p, format); + string_ptr = xvasprintf(format, p); + va_end(p); + + std::string res = string_ptr; + free(string_ptr); + return res; +} + //static inline void store_val(DBusMessageIter* iter, bool val) { store_bool(iter, val); } static inline void store_val(DBusMessageIter* iter, int32_t val) { store_int32(iter, val); } static inline void store_val(DBusMessageIter* iter, uint32_t val) { store_uint32(iter, val); } @@ -147,7 +188,7 @@ struct abrt_dbus_type< std::map<K,V> > { static std::string sig() { return ssprintf("a{%s%s}", ABRT_DBUS_SIG(K), ABRT_DBUS_SIG(V)); } }; -template<typename E> +template <typename E> static void store_vector(DBusMessageIter* iter, const std::vector<E>& val) { DBusMessageIter sub_iter; @@ -170,7 +211,7 @@ static void store_vector(DBus::MessageIter &iter, const std::vector<uint8_t>& va if we use such vector, MUST add specialized code here (see in dbus-c++ source) } */ -template<typename K, typename V> +template <typename K, typename V> static void store_map(DBusMessageIter* iter, const std::map<K,V>& val) { DBusMessageIter sub_iter; @@ -195,9 +236,9 @@ static void store_map(DBusMessageIter* iter, const std::map<K,V>& val) die_out_of_memory(); } -template<typename E> +template <typename E> static inline void store_val(DBusMessageIter* iter, const std::vector<E>& val) { store_vector(iter, val); } -template<typename K, typename V> +template <typename K, typename V> static inline void store_val(DBusMessageIter* iter, const std::map<K,V>& val) { store_map(iter, val); } @@ -220,7 +261,7 @@ static inline int load_val(DBusMessageIter* iter, std::string& val) } /* Templates for vector and map */ -template<typename E> +template <typename E> static int load_vector(DBusMessageIter* iter, std::vector<E>& val) { int type = dbus_message_iter_get_arg_type(iter); @@ -259,7 +300,7 @@ static int load_vector(DBusMessageIter* iter, std::vector<uint8_t>& val) if we use such vector, MUST add specialized code here (see in dbus-c++ source) } */ -template<typename K, typename V> +template <typename K, typename V> static int load_map(DBusMessageIter* iter, std::map<K,V>& val) { int type = dbus_message_iter_get_arg_type(iter); @@ -314,9 +355,9 @@ static int load_map(DBusMessageIter* iter, std::map<K,V>& val) return dbus_message_iter_next(iter); } -template<typename E> +template <typename E> static inline int load_val(DBusMessageIter* iter, std::vector<E>& val) { return load_vector(iter, val); } -template<typename K, typename V> +template <typename K, typename V> static inline int load_val(DBusMessageIter* iter, std::map<K,V>& val) { return load_map(iter, val); } #endif /* __cplusplus */ diff --git a/src/lib/Plugin.cpp b/src/lib/abrt_types.c index 0c2137f5..42100075 100644 --- a/src/lib/Plugin.cpp +++ b/src/lib/abrt_types.c @@ -1,6 +1,6 @@ /* - Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com) - Copyright (C) 2009 RedHat inc. + Copyright (C) 2010 ABRT Team + Copyright (C) 2010 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 @@ -16,26 +16,22 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "plugin.h" #include "abrtlib.h" -CPlugin::CPlugin() {} +map_string_h *new_map_string(void) +{ + return g_hash_table_new_full(g_str_hash, g_str_equal, free, free); +} + +void free_map_string(map_string_h *ms) +{ + if (ms) + g_hash_table_destroy(ms); +} -/* class CPlugin's virtuals */ -CPlugin::~CPlugin() {} -void CPlugin::Init() {} -void CPlugin::DeInit() {} -void CPlugin::SetSettings(const map_plugin_settings_t& pSettings) +const char *get_map_string_item_or_empty(map_string_h *ms, const char *key) { - m_pSettings = pSettings; - VERB3 - { - log("SetSettings:"); - map_plugin_settings_t::const_iterator it = m_pSettings.begin(); - while (it != m_pSettings.end()) - { - log(" settings[%s]='%s'", it->first.c_str(), it->second.c_str()); - it++; - } - } + const char *v = (const char*)g_hash_table_lookup(ms, key); + if (!v) v = ""; + return v; } diff --git a/src/lib/abrt_xmlrpc.cpp b/src/lib/abrt_xmlrpc.cpp index bf74f05b..e2b8674c 100644 --- a/src/lib/abrt_xmlrpc.cpp +++ b/src/lib/abrt_xmlrpc.cpp @@ -18,15 +18,10 @@ */ #include "abrtlib.h" #include "abrt_xmlrpc.h" -#include "abrt_exception.h" void throw_xml_fault(xmlrpc_env *env) { - std::string errmsg = ssprintf("XML-RPC Fault(%d): %s", env->fault_code, env->fault_string); - xmlrpc_env_clean(env); // this is needed ONLY if fault_occurred - xmlrpc_env_init(env); // just in case user catches ex and _continues_ to use env - error_msg("%s", errmsg.c_str()); // show error in daemon log - throw CABRTException(EXCEP_PLUGIN, errmsg.c_str()); + error_msg_and_die("XML-RPC Fault(%d): %s", env->fault_code, env->fault_string); } void throw_if_xml_fault_occurred(xmlrpc_env *env) @@ -49,6 +44,17 @@ void abrt_xmlrpc_conn::new_xmlrpc_client(const char* url, bool ssl_verify) * We do it in abrtd's main */ /* xmlrpc_client_setup_global_const(&env); */ + /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of + * XML-RPC response from server + * + * By default, XML data from the network may be no larger than 512K. + * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h + * + * Users reported trouble with 733402 byte long responses, hope raising the + * limit to 2*512k is enough + */ + xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT); + struct xmlrpc_curl_xportparms curlParms; memset(&curlParms, 0, sizeof(curlParms)); /* curlParms.network_interface = NULL; - done by memset */ diff --git a/src/lib/abrt_xmlrpc.h b/src/lib/abrt_xmlrpc.h index ad1a87d3..93c5a9d6 100644 --- a/src/lib/abrt_xmlrpc.h +++ b/src/lib/abrt_xmlrpc.h @@ -23,12 +23,12 @@ #include <xmlrpc-c/base.h> #include <xmlrpc-c/client.h> +#ifdef __cplusplus /* * Simple class holding XMLRPC connection data. * Used mainly to ensure we always destroy xmlrpc client and server_info * on return or throw. */ - struct abrt_xmlrpc_conn { xmlrpc_client* m_pClient; xmlrpc_server_info* m_pServer_info; @@ -40,9 +40,19 @@ struct abrt_xmlrpc_conn { void new_xmlrpc_client(const char* url, bool ssl_verify); void destroy_xmlrpc_client(); }; +#endif + + +#ifdef __cplusplus +extern "C" { +#endif /* Utility functions */ void throw_xml_fault(xmlrpc_env *env); void throw_if_xml_fault_occurred(xmlrpc_env *env); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/lib/binhex.c b/src/lib/binhex.c new file mode 100644 index 00000000..1fcb7445 --- /dev/null +++ b/src/lib/binhex.c @@ -0,0 +1,74 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 RedHat Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 "abrtlib.h" + +static const char hexdigits_locase[] = "0123456789abcdef"; + +/* Emit a string of hex representation of bytes */ +char *bin2hex(char *dst, const char *str, int count) +{ + while (count) { + unsigned char c = *str++; + /* put lowercase hex digits */ + *dst++ = hexdigits_locase[c >> 4]; + *dst++ = hexdigits_locase[c & 0xf]; + count--; + } + return dst; +} + +/* Convert "xxxxxxxx" hex string to binary, no more than COUNT bytes */ +char *hex2bin(char *dst, const char *str, int count) +{ + /* Parts commented out with // allow parsing + * of strings like "xx:x:x:xx:xx:xx:xxxxxx" + * (IPv6, ethernet addresses and the like). + */ + errno = EINVAL; + while (*str && count) { + uint8_t val; + uint8_t c; + + c = *str++; + if (isdigit(c)) + val = c - '0'; + else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') + val = (c|0x20) - ('a' - 10); + else + return NULL; + val <<= 4; + c = *str; + if (isdigit(c)) + val |= c - '0'; + else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') + val |= (c|0x20) - ('a' - 10); + //else if (c == ':' || c == '\0') + // val >>= 4; + else + return NULL; + + *dst++ = val; + //if (c != '\0') + str++; + //if (*str == ':') + // str++; + count--; + } + errno = (*str ? ERANGE : 0); + return dst; +} diff --git a/src/lib/copy_file_recursive.c b/src/lib/copy_file_recursive.c new file mode 100644 index 00000000..c3f021c7 --- /dev/null +++ b/src/lib/copy_file_recursive.c @@ -0,0 +1,139 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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 "abrtlib.h" + +int copy_file_recursive(const char *source, const char *dest) +{ + /* This is a recursive function, try to minimize stack usage */ + /* NB: each struct stat is ~100 bytes */ + struct stat source_stat; + struct stat dest_stat; + int retval = 0; + int dest_exists = 0; + + if (strcmp(source, ".lock") == 0) + goto skip; + + if (stat(source, &source_stat) < 0) { + perror_msg("Can't stat '%s'", source); + return -1; + } + + if (lstat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("Can't stat '%s'", dest); + return -1; + } + } else { + if (source_stat.st_dev == dest_stat.st_dev + && source_stat.st_ino == dest_stat.st_ino + ) { + error_msg("'%s' and '%s' are the same file", source, dest); + return -1; + } + dest_exists = 1; + } + + if (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + struct dirent *d; + + if (dest_exists) { + if (!S_ISDIR(dest_stat.st_mode)) { + error_msg("Target '%s' is not a directory", dest); + return -1; + } + /* race here: user can substitute a symlink between + * this check and actual creation of files inside dest */ + } else { + /* Create DEST */ + mode_t mode = source_stat.st_mode; + /* Allow owner to access new dir (at least for now) */ + mode |= S_IRWXU; + if (mkdir(dest, mode) < 0) { + perror_msg("Can't create directory '%s'", dest); + return -1; + } + } + /* Recursively copy files in SOURCE */ + dp = opendir(source); + if (dp == NULL) { + retval = -1; + goto ret; + } + + while (retval == 0 && (d = readdir(dp)) != NULL) { + char *new_source, *new_dest; + + if (dot_or_dotdot(d->d_name)) + continue; + new_source = concat_path_file(source, d->d_name); + new_dest = concat_path_file(dest, d->d_name); + if (copy_file_recursive(new_source, new_dest) < 0) + retval = -1; + free(new_source); + free(new_dest); + } + closedir(dp); + + goto ret; + } + + if (S_ISREG(source_stat.st_mode)) { + int src_fd; + int dst_fd; + mode_t new_mode; + + src_fd = open(source, O_RDONLY); + if (src_fd < 0) { + perror_msg("Can't open '%s'", source); + return -1; + } + + /* Do not try to open with weird mode fields */ + new_mode = source_stat.st_mode; + + // security problem versus (sym)link attacks + // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); + /* safe way: */ + dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); + if (dst_fd < 0) { + close(src_fd); + return -1; + } + + if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1) + retval = -1; + close(src_fd); + /* Careful: do check that buffered writes succeeded... */ + if (close(dst_fd) < 0) { + perror_msg("Error writing to '%s'", dest); + retval = -1; + } + goto ret; + } + + /* Neither dir not regular file: skip */ + + skip: + log("Skipping '%s'", source); + ret: + return retval; +} diff --git a/src/lib/crash_dump.cpp b/src/lib/crash_data.c index 338724d5..1ae8f4f1 100644 --- a/src/lib/crash_dump.cpp +++ b/src/lib/crash_data.c @@ -17,20 +17,77 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "abrtlib.h" -#include "abrt_crash_dump.h" - -const char *const must_have_files[] = { - FILENAME_ARCHITECTURE, - FILENAME_KERNEL , - FILENAME_PACKAGE , - FILENAME_COMPONENT , - FILENAME_RELEASE , - FILENAME_EXECUTABLE , - NULL -}; + +static void free_crash_item(void *ptr) +{ + if (ptr) + { + struct crash_item *item = (struct crash_item *)ptr; + free(item->content); + free(item); + } +} + + +/* crash_data["name"] = { "content", CD_FLAG_foo_bits } */ + +crash_data_t *new_crash_data(void) +{ + return g_hash_table_new_full(g_str_hash, g_str_equal, + free, free_crash_item); +} + +void add_to_crash_data_ext(crash_data_t *crash_data, + const char *name, + const char *content, + unsigned flags) +{ + if (!(flags & (CD_FLAG_BIN|CD_FLAG_TXT))) + flags |= CD_FLAG_TXT; + if (!(flags & (CD_FLAG_ISEDITABLE|CD_FLAG_ISNOTEDITABLE))) + flags |= CD_FLAG_ISNOTEDITABLE; + + struct crash_item *item = (struct crash_item *)xzalloc(sizeof(*item)); + item->content = xstrdup(content); + item->flags = flags; + g_hash_table_replace(crash_data, xstrdup(name), item); +} + +void add_to_crash_data(crash_data_t *crash_data, + const char *name, + const char *content) +{ + add_to_crash_data_ext(crash_data, name, content, CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE); +} + +const char *get_crash_item_content_or_die(crash_data_t *crash_data, const char *key) +{ + struct crash_item *item = get_crash_data_item_or_NULL(crash_data, key); + if (!item) + error_msg_and_die("Error accessing crash data: no ['%s']", key); + return item->content; +} + +const char *get_crash_item_content_or_NULL(crash_data_t *crash_data, const char *key) +{ + struct crash_item *item = get_crash_data_item_or_NULL(crash_data, key); + if (!item) + return NULL; + return item->content; +} + + +/* crash_data_vector[i] = { "name" = { "content", CD_FLAG_foo_bits } } */ + +vector_of_crash_data_t *new_vector_of_crash_data(void) +{ + return g_ptr_array_new_with_free_func((void (*)(void*)) &free_crash_data); +} + + +/* Miscellaneous helpers */ static const char *const editable_files[] = { - FILENAME_DESCRIPTION, FILENAME_COMMENT , FILENAME_REPRODUCE , FILENAME_BACKTRACE , @@ -52,36 +109,6 @@ bool is_editable_file(const char *file_name) return is_editable(file_name, editable_files); } - -void add_to_crash_data_ext(map_crash_data_t& pCrashData, - const char *pItem, - const char *pType, - const char *pEditable, - const char *pContent) -{ - map_crash_data_t::iterator it = pCrashData.find(pItem); - if (it == pCrashData.end()) { - vector_string_t& v = pCrashData[pItem]; /* create empty vector */ - v.push_back(pType); - v.push_back(pEditable); - v.push_back(pContent); - return; - } - vector_string_t& v = it->second; - while (v.size() < 3) - v.push_back(""); - v[CD_TYPE] = pType; - v[CD_EDITABLE] = pEditable; - v[CD_CONTENT] = pContent; -} - -void add_to_crash_data(map_crash_data_t& pCrashData, - const char *pItem, - const char *pContent) -{ - add_to_crash_data_ext(pCrashData, pItem, CD_TXT, CD_ISNOTEDITABLE, pContent); -} - static char* is_text_file(const char *name, ssize_t *sz) { /* We were using magic.h API to check for file being text, but it thinks @@ -156,7 +183,7 @@ static char* is_text_file(const char *name, ssize_t *sz) return NULL; /* it's binary */ } -void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t& data) +void load_crash_data_from_dump_dir(crash_data_t *crash_data, struct dump_dir *dd) { char *short_name; char *full_name; @@ -173,13 +200,11 @@ void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t& text = is_text_file(full_name, &sz); if (!text) { - add_to_crash_data_ext(data, + add_to_crash_data_ext(crash_data, short_name, - CD_BIN, - CD_ISNOTEDITABLE, - full_name + full_name, + CD_FLAG_BIN + CD_FLAG_ISNOTEDITABLE ); - free(short_name); free(full_name); continue; @@ -193,11 +218,10 @@ void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t& content = dd_load_text(dd, short_name); free(text); - add_to_crash_data_ext(data, + add_to_crash_data_ext(crash_data, short_name, - CD_TXT, - editable ? CD_ISEDITABLE : CD_ISNOTEDITABLE, - content + content, + (editable ? CD_FLAG_TXT + CD_FLAG_ISEDITABLE : CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE) ); free(short_name); free(full_name); @@ -205,50 +229,25 @@ void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t& } } -static const std::string* helper_get_crash_data_item_content(const map_crash_data_t& crash_data, const char *key) +crash_data_t *create_crash_data_from_dump_dir(struct dump_dir *dd) { - map_crash_data_t::const_iterator it = crash_data.find(key); - if (it == crash_data.end()) { - return NULL; - } - if (it->second.size() <= CD_CONTENT) { - return NULL; - } - return &it->second[CD_CONTENT]; + crash_data_t *crash_data = new_crash_data(); + load_crash_data_from_dump_dir(crash_data, dd); + return crash_data; } -const std::string& get_crash_data_item_content(const map_crash_data_t& crash_data, const char *key) +void log_crash_data(crash_data_t *crash_data, const char *pfx) { - const std::string* sp = helper_get_crash_data_item_content(crash_data, key); - if (sp == NULL) { - if (crash_data.find(key) == crash_data.end()) - error_msg_and_die("Error accessing crash data: no ['%s']", key); - error_msg_and_die("Error accessing crash data: no ['%s'][%d]", key, CD_CONTENT); - } - return *sp; -} - -const char *get_crash_data_item_content_or_NULL(const map_crash_data_t& crash_data, const char *key) -{ - const std::string* sp = helper_get_crash_data_item_content(crash_data, key); - if (!sp) { - return NULL; - } - return sp->c_str(); -} - -void log_map_crash_data(const map_crash_data_t& data, const char *name) -{ - map_crash_data_t::const_iterator it = data.begin(); - while (it != data.end()) - { - ssize_t sz = it->second.size(); - log("%s[%s]:%s/%s/'%.20s'", - name, it->first.c_str(), - sz > 0 ? it->second[0].c_str() : "<NO [0]>", - sz > 1 ? it->second[1].c_str() : "<NO [1]>", - sz > 2 ? it->second[2].c_str() : "<NO [2]>" - ); - it++; - } + GHashTableIter iter; + char *name; + struct crash_item *value; + g_hash_table_iter_init(&iter, crash_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + log("%s[%s]:'%s' 0x%x", + pfx, name, + value->content, + value->flags + ); + } } diff --git a/src/lib/create_dump_dir.c b/src/lib/create_dump_dir.c new file mode 100644 index 00000000..cbacdab6 --- /dev/null +++ b/src/lib/create_dump_dir.c @@ -0,0 +1,85 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 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 "abrtlib.h" + +static struct dump_dir *try_dd_create(const char *base_dir_name, const char *dir_name) +{ + char *path = concat_path_file(base_dir_name, dir_name); + struct dump_dir *dd = dd_create(path, (uid_t)-1L); + if (dd) + dd_create_basic_files(dd, (uid_t)-1L); + free(path); + return dd; +} + +struct dump_dir *create_dump_dir_from_crash_data(crash_data_t *crash_data, const char *base_dir_name) +{ + char dir_name[sizeof("abrt-tmp-YYYY-MM-DD-HH:MM:SS-%lu") + sizeof(long)*3]; + sprintf(dir_name, "abrt-tmp-%s-%lu", iso_date_string(NULL), (long)getpid()); + + struct dump_dir *dd; + if (base_dir_name) + dd = try_dd_create(base_dir_name, dir_name); + else + { + /* Try /var/run/abrt */ + dd = try_dd_create(LOCALSTATEDIR"/run/abrt", dir_name); + /* Try $HOME/tmp */ + if (!dd) + { + char *home = getenv("HOME"); + if (home && home[0]) + { + home = concat_path_file(home, "tmp"); + /*mkdir(home, 0777); - do we want this? */ + dd = try_dd_create(home, dir_name); + free(home); + } + } +//TODO: try user's home dir obtained by getpwuid(getuid())? + /* Try /tmp */ + if (!dd) + dd = try_dd_create("/tmp", dir_name); + } + if (!dd) + return NULL; + + GHashTableIter iter; + char *name; + struct crash_item *value; + g_hash_table_iter_init(&iter, crash_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + if (name[0] == '.' || strchr(name, '/')) + { + error_msg("Crash data field name contains disallowed chars: '%s'", name); + goto next; + } + +//FIXME: what to do with CD_FLAG_BINs?? + if (value->flags & CD_FLAG_BIN) + goto next; + + dd_save_text(dd, name, value->content); + next: ; + } + + return dd; +} diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c index 19f86072..a84e2814 100644 --- a/src/lib/dump_dir.c +++ b/src/lib/dump_dir.c @@ -1,6 +1,4 @@ /* - DebugDump.cpp - Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com) Copyright (C) 2009 RedHat inc. @@ -22,11 +20,70 @@ #include "abrtlib.h" #include "strbuf.h" -// TODO: +// Locking logic: +// +// The directory is locked by creating a symlink named .lock inside it, +// whose value (where it "points to") is the pid of locking process. +// We use symlink, not an ordinary file, because symlink creation +// is an atomic operation. +// +// There are two cases where after .lock creation, we might discover +// that directory is not really free: +// * another process just created new directory, but didn't manage +// to lock it before us. +// * another process is deleting the directory, and we managed to sneak in +// and create .lock after it deleted all files (including .lock) +// but before it rmdir'ed the empty directory. +// +// Both these cases are detected by the fact that file named "time" +// is not present (it must be present in any valid dump dir). +// If after locking the dir we don't see time file, we remove the lock +// at once and back off. What happens in concurrent processes +// we interfered with? +// * "create new dump dir" process just re-tries locking. +// * "delete dump dir" process just retries rmdir. // -// Perhaps dd_opendir should do some sanity checking like -// "if there is no "uid" file in the directory, it's not a crash dump", -// and fail. +// There is another case when we don't find time file: +// when the directory is not really a *dump* dir - user gave us +// an ordinary directory name by mistake. +// We detect it by bailing out of "lock, check time file; sleep +// and retry if it doesn't exist" loop using a counter. +// +// To make locking work reliably, it's important to set timeouts +// correctly. For example, dd_create should retry locking +// its newly-created directory much faster than dd_opendir +// tries to lock the directory it tries to open. + + +// How long to sleep between "symlink fails with EEXIST, +// readlink fails with ENOENT" tries. Someone just unlocked the dir. +// We never bail out in this case, we retry forever. +// The value can be really small: +#define SYMLINK_RETRY_USLEEP (10*1000) + +// How long to sleep when lock file with valid pid is seen by dd_opendir +// (we are waiting for other process to unlock or die): +#define WAIT_FOR_OTHER_PROCESS_USLEEP (500*1000) + +// How long to sleep when lock file with valid pid is seen by dd_create +// (some idiot jumped the gun and locked the dir we just created). +// Must not be the same as WAIT_FOR_OTHER_PROCESS_USLEEP (we depend on this) +// and should be small (we have the priority in locking, this is OUR dir): +#define CREATE_LOCK_USLEEP (10*1000) + +// How long to sleep after we locked a dir, found no time file +// (either we are racing with someone, or it's not a dump dir) +// and unlocked it; +// and after how many tries to give up and declare it's not a dump dir: +#define NO_TIME_FILE_USLEEP (50*1000) +#define NO_TIME_FILE_COUNT 10 + +// How long to sleep after we unlocked an empty dir, but then rmdir failed +// (some idiot jumped the gun and locked the dir we are deleting); +// and after how many tries to give up: +#define RMDIR_FAIL_USLEEP (10*1000) +#define RMDIR_FAIL_COUNT 50 + static char *load_text_file(const char *path, unsigned flags); @@ -53,12 +110,21 @@ static bool exist_file_dir(const char *path) return false; } -static bool get_and_set_lock(const char* lock_file, const char* pid) +/* Return values: + * -1: error + * 0: failed to lock (someone else has it locked) + * 1: success + */ +static int get_and_set_lock(const char* lock_file, const char* pid) { while (symlink(pid, lock_file) != 0) { if (errno != EEXIST) - perror_msg_and_die("Can't create lock file '%s'", lock_file); + { + if (errno != ENOENT && errno != ENOTDIR) + perror_msg("Can't create lock file '%s'", lock_file); + return -1; + } char pid_buf[sizeof(pid_t)*3 + 4]; ssize_t r = readlink(lock_file, pid_buf, sizeof(pid_buf) - 1); @@ -67,54 +133,94 @@ static bool get_and_set_lock(const char* lock_file, const char* pid) if (errno == ENOENT) { /* Looks like lock_file was deleted */ - usleep(10 * 1000); /* avoid CPU eating loop */ + usleep(SYMLINK_RETRY_USLEEP); /* avoid CPU eating loop */ continue; } - perror_msg_and_die("Can't read lock file '%s'", lock_file); + perror_msg("Can't read lock file '%s'", lock_file); + return -1; } pid_buf[r] = '\0'; if (strcmp(pid_buf, pid) == 0) { log("Lock file '%s' is already locked by us", lock_file); - return false; + return 0; } if (isdigit_str(pid_buf)) { - char pid_str[sizeof("/proc/") + strlen(pid_buf)]; + char pid_str[sizeof("/proc/") + sizeof(pid_buf)]; sprintf(pid_str, "/proc/%s", pid_buf); if (access(pid_str, F_OK) == 0) { log("Lock file '%s' is locked by process %s", lock_file, pid_buf); - return false; + return 0; } log("Lock file '%s' was locked by process %s, but it crashed?", lock_file, pid_buf); } /* The file may be deleted by now by other process. Ignore ENOENT */ if (unlink(lock_file) != 0 && errno != ENOENT) { - perror_msg_and_die("Can't remove stale lock file '%s'", lock_file); + perror_msg("Can't remove stale lock file '%s'", lock_file); + return -1; } } VERB1 log("Locked '%s'", lock_file); - return true; + return 1; } -static void dd_lock(struct dump_dir *dd) +static int dd_lock(struct dump_dir *dd, unsigned sleep_usec) { if (dd->locked) - error_msg_and_die("Locking bug on '%s'", dd->dd_dir); - - char lock_buf[strlen(dd->dd_dir) + sizeof(".lock")]; - sprintf(lock_buf, "%s.lock", dd->dd_dir); + error_msg_and_die("Locking bug on '%s'", dd->dd_dirname); char pid_buf[sizeof(long)*3 + 2]; sprintf(pid_buf, "%lu", (long)getpid()); - while ((dd->locked = get_and_set_lock(lock_buf, pid_buf)) != true) + + unsigned dirname_len = strlen(dd->dd_dirname); + char lock_buf[dirname_len + sizeof("/.lock")]; + strcpy(lock_buf, dd->dd_dirname); + strcpy(lock_buf + dirname_len, "/.lock"); + + unsigned count = NO_TIME_FILE_COUNT; + retry: + while (1) { - sleep(1); /* was 0.5 seconds */ + int r = get_and_set_lock(lock_buf, pid_buf); + if (r < 0) + return r; /* error */ + if (r > 0) + break; /* locked successfully */ + /* Other process has the lock, wait for it to go away */ + usleep(sleep_usec); } + + /* Are we called by dd_opendir (as opposed to dd_create)? */ + if (sleep_usec == WAIT_FOR_OTHER_PROCESS_USLEEP) /* yes */ + { + strcpy(lock_buf + dirname_len, "/time"); + if (access(lock_buf, F_OK) != 0) + { + /* time file doesn't exist. We managed to lock the directory + * which was just created by somebody else, or is almost deleted + * by delete_file_dir. + * Unlock and back off. + */ + strcpy(lock_buf + dirname_len, "/.lock"); + xunlink(lock_buf); + VERB1 log("Unlocked '%s' (no time file)", lock_buf); + if (--count == 0) + { + errno = EISDIR; /* "this is an ordinary dir, not dump dir" */ + return -1; + } + usleep(NO_TIME_FILE_USLEEP); + goto retry; + } + } + + dd->locked = true; + return 0; } static void dd_unlock(struct dump_dir *dd) @@ -122,9 +228,13 @@ static void dd_unlock(struct dump_dir *dd) if (dd->locked) { dd->locked = 0; - char lock_buf[strlen(dd->dd_dir) + sizeof(".lock")]; - sprintf(lock_buf, "%s.lock", dd->dd_dir); + + unsigned dirname_len = strlen(dd->dd_dirname); + char lock_buf[dirname_len + sizeof("/.lock")]; + strcpy(lock_buf, dd->dd_dirname); + strcpy(lock_buf + dirname_len, "/.lock"); xunlink(lock_buf); + VERB1 log("Unlocked '%s'", lock_buf); } } @@ -136,7 +246,7 @@ static inline struct dump_dir *dd_init(void) int dd_exist(struct dump_dir *dd, const char *path) { - char *full_path = concat_path_file(dd->dd_dir, path); + char *full_path = concat_path_file(dd->dd_dirname, path); int ret = exist_file_dir(full_path); free(full_path); return ret; @@ -154,7 +264,7 @@ void dd_close(struct dump_dir *dd) /* free(dd->next_dir); - WRONG! */ } - free(dd->dd_dir); + free(dd->dd_dirname); free(dd); } @@ -170,52 +280,68 @@ struct dump_dir *dd_opendir(const char *dir, int flags) { struct dump_dir *dd = dd_init(); - /* Used to use rm_trailing_slashes(dir) here, but with dir = "." - * or "..", or if the last component is a symlink, - * then lock file is created in the wrong place. - * IOW: this breaks locking. - */ - dd->dd_dir = realpath(dir, NULL); - if (!dd->dd_dir) - { - if (!(flags & DD_FAIL_QUIETLY)) - error_msg("'%s' does not exist", dir); - dd_close(dd); - return NULL; - } - dir = dd->dd_dir; - - dd_lock(dd); + dir = dd->dd_dirname = rm_trailing_slashes(dir); - struct stat stat_buf; - if (stat(dir, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode)) + errno = 0; + if (dd_lock(dd, WAIT_FOR_OTHER_PROCESS_USLEEP) < 0) { - if (!(flags & DD_FAIL_QUIETLY)) - error_msg("'%s' does not exist", dir); + if ((flags & DD_OPEN_READONLY) && errno == EACCES) + { + /* Directory is not writable. If it seems to be readable, + * return "read only" dd, not NULL */ + struct stat stat_buf; + if (stat(dir, &stat_buf) == 0 + && S_ISDIR(stat_buf.st_mode) + && access(dir, R_OK) == 0 + ) { + return dd; + } + } + if (errno == EISDIR) + { + /* EISDIR: dd_lock can lock the dir, but it sees no time file there, + * even after it retried many times. It must be an ordinary directory! + * + * Without this check, e.g. abrt-action-print happily prints any current + * directory when run without arguments, because its option -d DIR + * defaults to "."! + */ + /*if (!(flags & DD_FAIL_QUIETLY))... - no, DD_FAIL_QUIETLY only means + * "it's ok if it doesn exist", not "ok if contents is bogus"! + */ + error_msg("'%s' is not a crash dump directory", dir); + dd_close(dd); + return NULL; + } + + if (errno == ENOENT || errno == ENOTDIR) + { + if (!(flags & DD_FAIL_QUIETLY)) + error_msg("'%s' does not exist", dir); + } + else + { + perror_msg("Can't access '%s'", dir); + } dd_close(dd); return NULL; } - /* In case caller would want to create more files, he'll need uid:gid */ - dd->dd_uid = stat_buf.st_uid; - dd->dd_gid = stat_buf.st_gid; - - /* Without this check, e.g. abrt-action-print happily prints any current - * directory when run without arguments, because its option -d DIR - * defaults to "."! Let's require that at least some crash dump dir - * specific files exist before we declare open successful: - */ - char *name = concat_path_file(dir, FILENAME_ANALYZER); - int bad = (lstat(name, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)); - free(name); - if (bad) + dd->dd_uid = (uid_t)-1L; + dd->dd_gid = (gid_t)-1L; + if (geteuid() == 0) { - /*if (!(flags & DD_FAIL_QUIETLY))... - no, DD_FAIL_QUIETLY only means - * "it's ok if it doesn exist", not "ok if contents is bogus"! - */ - error_msg("'%s' is not a crash dump directory", dir); - dd_close(dd); - return NULL; + /* In case caller would want to create more files, he'll need uid:gid */ + struct stat stat_buf; + if (stat(dir, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode)) + { + if (!(flags & DD_FAIL_QUIETLY)) + error_msg("'%s' does not exist", dir); + dd_close(dd); + return NULL; + } + dd->dd_uid = stat_buf.st_uid; + dd->dd_gid = stat_buf.st_gid; } return dd; @@ -248,7 +374,7 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) * realpath will always return NULL. We don't really have to: * dd_opendir(".") makes sense, dd_create(".") does not. */ - dir = dd->dd_dir = rm_trailing_slashes(dir); + dir = dd->dd_dirname = rm_trailing_slashes(dir); const char *last_component = strrchr(dir, '/'); if (last_component) @@ -265,15 +391,38 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) return NULL; } - dd_lock(dd); - + bool created_parents = false; + try_again: /* Was creating it with mode 0700 and user as the owner, but this allows * the user to replace any file in the directory, changing security-sensitive data * (e.g. "uid", "analyzer", "executable") */ if (mkdir(dir, 0750) == -1) { - perror_msg("Can't create dir '%s'", dir); + int err = errno; + if (!created_parents && errno == ENOENT) + { + char *p = dd->dd_dirname + 1; + while ((p = strchr(p, '/')) != NULL) + { + *p = '\0'; + int r = (mkdir(dd->dd_dirname, 0755) == 0 || errno == EEXIST); + *p++ = '/'; + if (!r) + goto report_err; + } + created_parents = true; + goto try_again; + } + report_err: + errno = err; + perror_msg("Can't create directory '%s'", dir); + dd_close(dd); + return NULL; + } + + if (dd_lock(dd, CREATE_LOCK_USLEEP) < 0) + { dd_close(dd); return NULL; } @@ -281,35 +430,51 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) /* mkdir's mode (above) can be affected by umask, fix it */ if (chmod(dir, 0750) == -1) { - perror_msg("Can't change mode of '%s'", dir); + perror_msg("can't change mode of '%s'", dir); dd_close(dd); return NULL; } - /* Get ABRT's user id */ - /*dd->dd_uid = 0; - dd_init did this already */ - struct passwd *pw = getpwnam("abrt"); - if (pw) - dd->dd_uid = pw->pw_uid; - else - error_msg("User 'abrt' does not exist, using uid 0"); - - /* Get crashed application's group id */ - /*dd->dd_gid = 0; - dd_init did this already */ - pw = getpwuid(uid); - if (pw) - dd->dd_gid = pw->pw_gid; - else - error_msg("User %lu does not exist, using gid 0", (long)uid); - - if (chown(dir, dd->dd_uid, dd->dd_gid) == -1) + dd->dd_uid = (uid_t)-1L; + dd->dd_gid = (gid_t)-1L; + if (uid != (uid_t)-1L) { - perror_msg("Can't change '%s' ownership to %lu:%lu", dir, - (long)dd->dd_uid, (long)dd->dd_gid); + /* Get ABRT's user id */ + dd->dd_uid = 0; + struct passwd *pw = getpwnam("abrt"); + if (pw) + dd->dd_uid = pw->pw_uid; + else + error_msg("user 'abrt' does not exist, using uid 0"); + + /* Get crashed application's group id */ + /*dd->dd_gid = 0; - dd_init did this already */ + pw = getpwuid(uid); + if (pw) + dd->dd_gid = pw->pw_gid; + else + error_msg("User %lu does not exist, using gid 0", (long)uid); + + if (chown(dir, dd->dd_uid, dd->dd_gid) == -1) + { + perror_msg("can't change '%s' ownership to %lu:%lu", dir, + (long)dd->dd_uid, (long)dd->dd_gid); + } } + return dd; +} + +void dd_create_basic_files(struct dump_dir *dd, uid_t uid) +{ char long_str[sizeof(long) * 3 + 2]; + time_t t = time(NULL); + sprintf(long_str, "%lu", (long)t); + dd_save_text(dd, FILENAME_TIME, long_str); + + if (uid == (uid_t)-1) + uid = getuid(); sprintf(long_str, "%lu", (long)uid); dd_save_text(dd, FILENAME_UID, long_str); @@ -317,54 +482,102 @@ struct dump_dir *dd_create(const char *dir, uid_t uid) uname(&buf); /* never fails */ dd_save_text(dd, FILENAME_KERNEL, buf.release); dd_save_text(dd, FILENAME_ARCHITECTURE, buf.machine); - char *release = load_text_file("/etc/redhat-release", /*flags:*/ 0); - strchrnul(release, '\n')[0] = '\0'; - dd_save_text(dd, FILENAME_RELEASE, release); - free(release); + dd_save_text(dd, FILENAME_HOSTNAME, buf.nodename); - time_t t = time(NULL); - sprintf(long_str, "%lu", (long)t); - dd_save_text(dd, FILENAME_TIME, long_str); - - return dd; + char *release = load_text_file("/etc/system-release", + DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE); + if (!release) + release = load_text_file("/etc/redhat-release", /*flags:*/ 0); + dd_save_text(dd, FILENAME_OS_RELEASE, release); + free(release); } -static void delete_file_dir(const char *dir) +static int delete_file_dir(const char *dir, bool skip_lock_file) { DIR *d = opendir(dir); if (!d) - return; + { + /* The caller expects us to error out only if the directory + * still exists (not deleted). If directory + * *doesn't exist*, return 0 and clear errno. + */ + if (errno == ENOENT || errno == ENOTDIR) + { + errno = 0; + return 0; + } + return -1; + } + bool unlink_lock_file = false; struct dirent *dent; while ((dent = readdir(d)) != NULL) { if (dot_or_dotdot(dent->d_name)) continue; + if (skip_lock_file && strcmp(dent->d_name, ".lock") == 0) + { + unlink_lock_file = true; + continue; + } char *full_path = concat_path_file(dir, dent->d_name); if (unlink(full_path) == -1 && errno != ENOENT) { - if (errno != EISDIR) + int err = 0; + if (errno == EISDIR) + { + errno = 0; + err = delete_file_dir(full_path, /*skip_lock_file:*/ false); + } + if (errno || err) { - error_msg("Can't remove '%s'", full_path); + perror_msg("Can't remove '%s'", full_path); free(full_path); closedir(d); - return; + return -1; } - delete_file_dir(full_path); } free(full_path); } closedir(d); - if (rmdir(dir) == -1) + + /* Here we know for sure that all files/subdirs we found via readdir + * were deleted successfully. If rmdir below fails, we assume someone + * is racing with us and created a new file. + */ + + if (unlink_lock_file) { - error_msg("Can't remove dir '%s'", dir); + char *full_path = concat_path_file(dir, ".lock"); + xunlink(full_path); + free(full_path); + + unsigned cnt = RMDIR_FAIL_COUNT; + do { + if (rmdir(dir) == 0) + return 0; + /* Someone locked the dir after unlink, but before rmdir. + * This "someone" must be dd_lock(). + * It detects this (by seeing that there is no time file) + * and backs off at once. So we need to just retry rmdir, + * with minimal sleep. + */ + usleep(RMDIR_FAIL_USLEEP); + } while (--cnt != 0); } + + int r = rmdir(dir); + if (r) + perror_msg("Can't remove directory '%s'", dir); + return r; } -void dd_delete(struct dump_dir *dd) +int dd_delete(struct dump_dir *dd) { - delete_file_dir(dd->dd_dir); + int r = delete_file_dir(dd->dd_dirname, /*skip_lock_file:*/ true); + dd->locked = 0; /* delete_file_dir already removed .lock */ dd_close(dd); + return r; } static char *load_text_file(const char *path, unsigned flags) @@ -411,10 +624,15 @@ static bool save_binary_file(const char *path, const char* data, unsigned size, perror_msg("Can't open file '%s'", path); return false; } - if (fchown(fd, uid, gid) == -1) + + if (uid != (uid_t)-1L) { - perror_msg("can't change '%s' ownership to %lu:%lu", path, (long)uid, (long)gid); + if (fchown(fd, uid, gid) == -1) + { + perror_msg("can't change '%s' ownership to %lu:%lu", path, (long)uid, (long)gid); + } } + unsigned r = full_write(fd, data, size); close(fd); if (r != size) @@ -428,10 +646,14 @@ static bool save_binary_file(const char *path, const char* data, unsigned size, char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags) { - if (!dd->locked) - error_msg_and_die("dump_dir is not opened"); /* bug */ +// if (!dd->locked) +// error_msg_and_die("dump_dir is not opened"); /* bug */ - char *full_path = concat_path_file(dd->dd_dir, name); + /* Compat with old abrt dumps. Remove in abrt-2.1 */ + if (strcmp(name, "release") == 0) + name = FILENAME_OS_RELEASE; + + char *full_path = concat_path_file(dd->dd_dirname, name); char *ret = load_text_file(full_path, flags); free(full_path); @@ -448,7 +670,7 @@ void dd_save_text(struct dump_dir *dd, const char *name, const char *data) if (!dd->locked) error_msg_and_die("dump_dir is not opened"); /* bug */ - char *full_path = concat_path_file(dd->dd_dir, name); + char *full_path = concat_path_file(dd->dd_dirname, name); save_binary_file(full_path, data, strlen(data), dd->dd_uid, dd->dd_gid); free(full_path); } @@ -458,23 +680,23 @@ void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, uns if (!dd->locked) error_msg_and_die("dump_dir is not opened"); /* bug */ - char *full_path = concat_path_file(dd->dd_dir, name); + char *full_path = concat_path_file(dd->dd_dirname, name); save_binary_file(full_path, data, size, dd->dd_uid, dd->dd_gid); free(full_path); } DIR *dd_init_next_file(struct dump_dir *dd) { - if (!dd->locked) - error_msg_and_die("dump_dir is not opened"); /* bug */ +// if (!dd->locked) +// error_msg_and_die("dump_dir is not opened"); /* bug */ if (dd->next_dir) closedir(dd->next_dir); - dd->next_dir = opendir(dd->dd_dir); + dd->next_dir = opendir(dd->dd_dirname); if (!dd->next_dir) { - error_msg("Can't open dir '%s'", dd->dd_dir); + error_msg("Can't open directory '%s'", dd->dd_dirname); } return dd->next_dir; @@ -488,12 +710,12 @@ int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name) struct dirent *dent; while ((dent = readdir(dd->next_dir)) != NULL) { - if (is_regular_file(dent, dd->dd_dir)) + if (is_regular_file(dent, dd->dd_dirname)) { if (short_name) *short_name = xstrdup(dent->d_name); if (full_name) - *full_name = concat_path_file(dd->dd_dir, dent->d_name); + *full_name = concat_path_file(dd->dd_dirname, dent->d_name); return 1; } } @@ -504,9 +726,9 @@ int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name) } /* Utility function */ -void delete_crash_dump_dir(const char *dd_dir) +void delete_dump_dir(const char *dirname) { - struct dump_dir *dd = dd_opendir(dd_dir, /*flags:*/ 0); + struct dump_dir *dd = dd_opendir(dirname, /*flags:*/ 0); if (dd) { dd_delete(dd); diff --git a/src/lib/ABRTException.cpp b/src/lib/glib_support.c index 0ae5d452..feb4c18b 100644 --- a/src/lib/ABRTException.cpp +++ b/src/lib/glib_support.c @@ -1,6 +1,6 @@ /* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 RedHat Inc + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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 @@ -16,18 +16,11 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "abrt_exception.h" +#include "abrtlib.h" -CABRTException::CABRTException(abrt_exception_t type, const char* fmt, ...) +void list_free_with_free(GList *list) { - m_type = type; - va_list ap; - va_start(ap, fmt); - m_what = xvasprintf(fmt, ap); - va_end(ap); + for (GList *li = list; li; li = g_list_next(li)) + free(li->data); + g_list_free(list); } - -CABRTException::CABRTException(const CABRTException& rhs): - m_type(rhs.m_type), - m_what(xstrdup(rhs.m_what)) -{} diff --git a/src/lib/hash_md5.h b/src/lib/hash_md5.h index cc1d2c43..f7e9f398 100644 --- a/src/lib/hash_md5.h +++ b/src/lib/hash_md5.h @@ -24,6 +24,9 @@ typedef struct md5_ctx_t { uint32_t buflen; char buffer[128]; } md5_ctx_t; +#define md5_begin abrt_md5_begin void md5_begin(md5_ctx_t *ctx); +#define md5_hash abrt_md5_hash void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); +#define md5_end abrt_md5_end void md5_end(void *resbuf, md5_ctx_t *ctx); diff --git a/src/lib/hash_sha1.h b/src/lib/hash_sha1.h index 02978ea4..09f50d12 100644 --- a/src/lib/hash_sha1.h +++ b/src/lib/hash_sha1.h @@ -31,8 +31,11 @@ typedef struct sha1_ctx_t { void (*process_block)(struct sha1_ctx_t*); } sha1_ctx_t; +#define sha1_begin abrt_sha1_begin void sha1_begin(sha1_ctx_t *ctx); +#define sha1_hash abrt_sha1_hash void sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx); +#define sha1_end abrt_sha1_end void sha1_end(void *resbuf, sha1_ctx_t *ctx); #ifdef __cplusplus diff --git a/src/lib/hooklib.c b/src/lib/hooklib.c index 63c8a634..a89ab008 100644 --- a/src/lib/hooklib.c +++ b/src/lib/hooklib.c @@ -132,7 +132,7 @@ void trim_debug_dumps(unsigned setting_MaxCrashReportsSize, const char *exclude_ char *d = concat_path_file(DEBUG_DUMPS_DIR, worst_dir); free(worst_dir); worst_dir = NULL; - delete_crash_dump_dir(d); + delete_dump_dir(d); free(d); } } diff --git a/src/lib/hooklib.h b/src/lib/hooklib.h index ba76efbc..84b31a5f 100644 --- a/src/lib/hooklib.h +++ b/src/lib/hooklib.h @@ -20,8 +20,14 @@ extern "C" { #endif -void parse_conf(const char *additional_conf, unsigned *setting_MaxCrashReportsSize, bool *setting_MakeCompatCore, bool *setting_SaveBinaryImage); +#define parse_conf abrt_parse_conf +void parse_conf(const char *additional_conf, + unsigned *setting_MaxCrashReportsSize, + bool *setting_MakeCompatCore, + bool *setting_SaveBinaryImage); +#define check_free_space abrt_check_free_space void check_free_space(unsigned setting_MaxCrashReportsSize); +#define trim_debug_dumps abrt_trim_debug_dumps void trim_debug_dumps(unsigned setting_MaxCrashReportsSize, const char *exclude_path); #ifdef __cplusplus diff --git a/src/lib/numtoa.cpp b/src/lib/iso_date_string.c index 061da553..4600ff7f 100644 --- a/src/lib/numtoa.cpp +++ b/src/lib/iso_date_string.c @@ -1,8 +1,6 @@ /* - Number to string conversions - - Copyright (C) 2010 ABRT team - Copyright (C) 2010 RedHat inc. + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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 @@ -18,17 +16,16 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + #include "abrtlib.h" -std::string unsigned_to_string(unsigned long long x) -{ - char buf[sizeof(x)*3]; - sprintf(buf, "%llu", x); - return buf; -} -std::string signed_to_string(long long x) +char *iso_date_string(time_t *pt) { - char buf[sizeof(x)*3]; - sprintf(buf, "%lld", x); - return buf; + static char buf[sizeof("YYYY-MM-DD-HH:MM:SS") + 4]; + + time_t t; + struct tm *ptm = localtime(pt ? pt : (time(&t), &t)); + strftime(buf, sizeof(buf), "%Y-%m-%d-%H:%M:%S", ptm); + + return buf; } diff --git a/src/lib/load_plugin_settings.cpp b/src/lib/load_plugin_settings.c index 1052f19e..1e6b31e7 100644 --- a/src/lib/load_plugin_settings.cpp +++ b/src/lib/load_plugin_settings.c @@ -18,8 +18,10 @@ */ #include "abrtlib.h" -bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings, - bool skipKeysWithoutValue /*= true*/) +/* Returns NULL if open failed. + * Returns empty hash if conf file is empty. + */ +bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWithoutValue) { FILE *fp = stdin; if (strcmp(pPath, "-") != 0) @@ -33,11 +35,15 @@ bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings, while ((line = xmalloc_fgetline(fp)) != NULL) { unsigned ii; - bool is_value = false; bool valid = false; bool in_quote = false; - std::string key; - std::string value; + /* 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++) { if (line[ii] == '"') @@ -48,47 +54,43 @@ bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings, { continue; } - if (line[ii] == '#' && !in_quote && key == "") + if (line[ii] == '#' && !in_quote && cur == line) { break; } if (line[ii] == '=' && !in_quote) { - is_value = true; valid = true; + *cur++ = '\0'; /* terminate key */ + value = cur; /* remember where value starts */ continue; } - if (!is_value) - { - key += line[ii]; - } - else - { - value += line[ii]; - } + *cur++ = line[ii]; /* store next key or value char */ } + *cur++ = '\0'; /* terminate value */ /* Skip broken or empty lines. */ if (!valid) goto free_line; /* Skip lines with empty key. */ - if (key.length() == 0) + if (key[0] == '\0') goto free_line; - if (skipKeysWithoutValue && value.length() == 0) + if (skipKeysWithoutValue && value[0] == '\0') goto free_line; /* Skip lines with unclosed quotes. */ if (in_quote) goto free_line; - pSettings[key] = value; + g_hash_table_replace(settings, xstrdup(key), xstrdup(value)); free_line: free(line); } if (fp != stdin) fclose(fp); + return true; } diff --git a/src/lib/logging.h b/src/lib/logging.h index 8a038bc7..316c1a22 100644 --- a/src/lib/logging.h +++ b/src/lib/logging.h @@ -33,14 +33,6 @@ extern "C" { #define NORETURN __attribute__ ((noreturn)) -/* VERB1 log("what you sometimes want to see, even on a production box") */ -#define VERB1 if (g_verbose >= 1) -/* VERB2 log("debug message, not going into insanely small details") */ -#define VERB2 if (g_verbose >= 2) -/* VERB3 log("lots and lots of details") */ -#define VERB3 if (g_verbose >= 3) -/* there is no level > 3 */ - enum { LOGMODE_NONE = 0, LOGMODE_STDIO = (1 << 0), @@ -49,26 +41,47 @@ enum { LOGMODE_CUSTOM = (1 << 2), }; +#define g_custom_logger abrt_g_custom_logger extern void (*g_custom_logger)(const char*); +#define msg_prefix abrt_msg_prefix extern const char *msg_prefix; +#define msg_eol abrt_msg_eol extern const char *msg_eol; +#define logmode abrt_logmode extern int logmode; +#define xfunc_error_retval abrt_xfunc_error_retval extern int xfunc_error_retval; /* Verbosity level */ +#define g_verbose abrt_g_verbose extern int g_verbose; +/* VERB1 log("what you sometimes want to see, even on a production box") */ +#define VERB1 if (g_verbose >= 1) +/* VERB2 log("debug message, not going into insanely small details") */ +#define VERB2 if (g_verbose >= 2) +/* VERB3 log("lots and lots of details") */ +#define VERB3 if (g_verbose >= 3) +/* there is no level > 3 */ +#define abrt_ +#define xfunc_die abrt_xfunc_die void xfunc_die(void) NORETURN; +#define log_msg abrt_log_msg void log_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); /* It's a macro, not function, since it collides with log() from math.h */ #undef log #define log(...) log_msg(__VA_ARGS__) /* error_msg family will use g_custom_logger. log_msg does not. */ +#define error_msg abrt_error_msg void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +#define error_msg_and_die abrt_error_msg_and_die void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); /* Reports error message with libc's errno error description attached. */ +#define perror_msg abrt_perror_msg void perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +#define perror_msg_and_die abrt_perror_msg_and_die void perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); +#define die_out_of_memory abrt_die_out_of_memory void die_out_of_memory(void) NORETURN; #ifdef __cplusplus diff --git a/src/lib/make_descr.cpp b/src/lib/make_descr.c index e11325c8..1ba15203 100644 --- a/src/lib/make_descr.cpp +++ b/src/lib/make_descr.c @@ -17,10 +17,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "abrtlib.h" -#include "abrt_crash_dump.h" - - -using namespace std; // caller is responsible for freeing **dsc static void add_content(bool *was_multiline, char **dsc, const char *header, const char *content) @@ -64,7 +60,6 @@ static void add_content(bool *was_multiline, char **dsc, const char *header, con static const char *const blacklisted_items[] = { FILENAME_ANALYZER , FILENAME_COREDUMP , - FILENAME_DESCRIPTION, /* package description - basically useless */ FILENAME_HOSTNAME , FILENAME_DUPHASH , FILENAME_UUID , @@ -75,30 +70,32 @@ static const char *const blacklisted_items[] = { NULL }; -char* make_description_mailx(const map_crash_data_t & crash_data) +char* make_description_mailx(crash_data_t *crash_data) { struct strbuf *buf_dsc = strbuf_new(); struct strbuf *buf_additional_files = strbuf_new(); struct strbuf *buf_duphash_file = strbuf_new(); struct strbuf *buf_common_files = strbuf_new(); - map_crash_data_t::const_iterator it; - for (it = crash_data.begin(); it != crash_data.end(); it++) + GHashTableIter iter; + char *name; + struct crash_item *value; + g_hash_table_iter_init(&iter, crash_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) { - if (it->second[CD_TYPE] == CD_TXT) + if (value->flags & CD_FLAG_TXT) { - const char *itemname = it->first.c_str(); - if ((strcmp(itemname, FILENAME_DUPHASH) != 0) - && (strcmp(itemname, FILENAME_ARCHITECTURE) != 0) - && (strcmp(itemname, FILENAME_KERNEL) != 0) - && (strcmp(itemname, FILENAME_PACKAGE) != 0) + if ((strcmp(name, FILENAME_DUPHASH) != 0) + && (strcmp(name, FILENAME_ARCHITECTURE) != 0) + && (strcmp(name, FILENAME_KERNEL) != 0) + && (strcmp(name, FILENAME_PACKAGE) != 0) ) { - strbuf_append_strf(buf_additional_files, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str()); + strbuf_append_strf(buf_additional_files, "%s\n-----\n%s\n\n", name, value->content); } - else if (strcmp(itemname, FILENAME_DUPHASH) == 0) - strbuf_append_strf(buf_duphash_file, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str()); + else if (strcmp(name, FILENAME_DUPHASH) == 0) + strbuf_append_strf(buf_duphash_file, "%s\n-----\n%s\n\n", name, value->content); else - strbuf_append_strf(buf_common_files, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str()); + strbuf_append_strf(buf_common_files, "%s\n-----\n%s\n\n", name, value->content); } } @@ -117,24 +114,27 @@ char* make_description_mailx(const map_crash_data_t & crash_data) return strbuf_free_nobuf(buf_dsc); } -char* make_description_bz(const map_crash_data_t& pCrashData) +char* make_description_bz(crash_data_t *crash_data) { struct strbuf *buf_dsc = strbuf_new(); struct strbuf *buf_long_dsc = strbuf_new(); - map_crash_data_t::const_iterator it = pCrashData.begin(); - for (; it != pCrashData.end(); it++) + GHashTableIter iter; + char *name; + struct crash_item *value; + g_hash_table_iter_init(&iter, crash_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) { - const char *itemname = it->first.c_str(); - const char *type = it->second[CD_TYPE].c_str(); - const char *content = it->second[CD_CONTENT].c_str(); - if (strcmp(type, CD_TXT) == 0) + struct stat statbuf; + unsigned flags = value->flags; + const char *content = value->content; + if (flags & CD_FLAG_TXT) { /* Skip items we are not interested in */ const char *const *bl = blacklisted_items; while (*bl) { - if (strcmp(itemname, *bl) == 0) + if (strcmp(name, *bl) == 0) break; bl++; } @@ -151,7 +151,7 @@ char* make_description_bz(const map_crash_data_t& pCrashData) add_content(&was_multiline, &tmp, /* "reproduce: blah" looks ugly, fixing: */ - (strcmp(itemname, FILENAME_REPRODUCE) == 0) ? "How to reproduce" : itemname, + (strcmp(name, FILENAME_REPRODUCE) == 0) ? "How to reproduce" : name, content ); @@ -167,13 +167,33 @@ char* make_description_bz(const map_crash_data_t& pCrashData) strbuf_append_str(buf_dsc, tmp); free(tmp); - } else { - bool was_multiline = 0; - char *dsc = NULL; - add_content(&was_multiline, &dsc, "Attached file", itemname); - strbuf_append_str(buf_dsc, dsc); - free(dsc); } + else + { + statbuf.st_size = strlen(content); + goto add_attachment_info; + } + } + if (flags & CD_FLAG_BIN) + { + /* In many cases, it is useful to know how big binary files are + * (for example, helps with diagnosing bug upload problems) + */ + if (stat(content, &statbuf) != 0) + statbuf.st_size = (off_t) -1; + + add_attachment_info: ; + char *descr; + if (statbuf.st_size >= 0) + descr = xasprintf("%s, %llu bytes", name, (long long)statbuf.st_size); + else + descr = xstrdup(name); + bool was_multiline = 0; + char *tmp = NULL; + add_content(&was_multiline, &tmp, "Attached file", descr); + free(descr); + strbuf_append_str(buf_dsc, tmp); + free(tmp); } } @@ -189,25 +209,25 @@ char* make_description_bz(const map_crash_data_t& pCrashData) return strbuf_free_nobuf(buf_dsc); } -char* make_description_logger(const map_crash_data_t& pCrashData) +char* make_description_logger(crash_data_t *crash_data) { struct strbuf *buf_dsc = strbuf_new(); struct strbuf *buf_long_dsc = strbuf_new(); - map_crash_data_t::const_iterator it = pCrashData.begin(); - for (; it != pCrashData.end(); it++) + GHashTableIter iter; + char *name; + struct crash_item *value; + g_hash_table_iter_init(&iter, crash_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) { - const char *filename = it->first.c_str(); - const char *type = it->second[CD_TYPE].c_str(); - const char *content = it->second[CD_CONTENT].c_str(); - if ((strcmp(type, CD_TXT) == 0) - || (strcmp(type, CD_BIN) == 0) - ) { + const char *content = value->content; + if (value->flags & (CD_FLAG_TXT|CD_FLAG_BIN)) + { /* Skip items we are not interested in */ const char *const *bl = blacklisted_items; while (*bl) { - if (filename == *bl) + if (name == *bl) break; bl++; } @@ -218,12 +238,12 @@ char* make_description_logger(const map_crash_data_t& pCrashData) bool was_multiline = 0; char *tmp = NULL; - add_content(&was_multiline, &tmp, filename, content); + add_content(&was_multiline, &tmp, name, content); if (was_multiline) { if (buf_long_dsc->len != 0) - strbuf_append_char(buf_long_dsc,'\n'); + strbuf_append_char(buf_long_dsc, '\n'); strbuf_append_str(buf_long_dsc, tmp); } @@ -242,29 +262,27 @@ char* make_description_logger(const map_crash_data_t& pCrashData) return strbuf_free_nobuf(buf_dsc); } -char* make_description_reproduce_comment(const map_crash_data_t& pCrashData) +char* make_description_reproduce_comment(crash_data_t *crash_data) { char *repro = NULL; char *comment = NULL; + struct crash_item *value; - map_crash_data_t::const_iterator end = pCrashData.end(); - map_crash_data_t::const_iterator it; - - it = pCrashData.find(FILENAME_REPRODUCE); - if (it != end) + value = get_crash_data_item_or_NULL(crash_data, FILENAME_REPRODUCE); + if (value) { - if ((it->second[CD_CONTENT].size() > 0) - && (it->second[CD_CONTENT] != "1.\n2.\n3.\n")) - { - repro = xasprintf("\n\nHow to reproduce\n-----\n%s", it->second[CD_CONTENT].c_str()); + if (value->content[0] + && strcmp(value->content, "1.\n2.\n3.\n") != 0 + ) { + repro = xasprintf("\n\nHow to reproduce\n-----\n%s", value->content); } } - it = pCrashData.find(FILENAME_COMMENT); - if (it != end) + value = get_crash_data_item_or_NULL(crash_data, FILENAME_COMMENT); + if (value) { - if (it->second[CD_CONTENT].size() > 0) - comment = xasprintf("\n\nComment\n-----\n%s", it->second[CD_CONTENT].c_str()); + if (value->content[0]) + comment = xasprintf("\n\nComment\n-----\n%s", value->content); } if (!repro && !comment) diff --git a/src/lib/parse_options.c b/src/lib/parse_options.c index c1a2c297..3d631461 100644 --- a/src/lib/parse_options.c +++ b/src/lib/parse_options.c @@ -1,3 +1,21 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 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 <getopt.h> #include "abrtlib.h" @@ -6,26 +24,17 @@ #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 -void parse_usage_and_die(const char *usage, const struct options *opt) +void show_usage_and_die(const char *usage, const struct options *opt) { fprintf(stderr, _("Usage: %s\n"), usage); - if (opt->type != OPTION_GROUP) - fputc('\n', stderr); + fputc('\n', stderr); for (; opt->type != OPTION_END; opt++) { size_t pos; int pad; - if (opt->type == OPTION_GROUP) - { - fputc('\n', stderr); - if (*opt->help) - fprintf(stderr, "%s\n", opt->help); - continue; - } - pos = fprintf(stderr, " "); if (opt->short_name) pos += fprintf(stderr, "-%c", opt->short_name); @@ -92,6 +101,7 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt, break; case OPTION_INTEGER: case OPTION_STRING: + case OPTION_LIST: curopt->has_arg = required_argument; if (opt[ii].short_name) strbuf_append_strf(shortopts, "%c:", opt[ii].short_name); @@ -101,7 +111,6 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt, if (opt[ii].short_name) strbuf_append_strf(shortopts, "%c::", opt[ii].short_name); break; - case OPTION_GROUP: case OPTION_END: break; } @@ -144,7 +153,7 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt, { free(longopts); strbuf_free(shortopts); - parse_usage_and_die(usage, opt); + show_usage_and_die(usage, opt); } for (ii = 0; ii < size; ++ii) @@ -157,17 +166,19 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt, if (opt[ii].value != NULL) switch (opt[ii].type) { case OPTION_BOOL: - *(int*)opt[ii].value += 1; + *(int*)(opt[ii].value) += 1; break; case OPTION_INTEGER: - *(int*)opt[ii].value = xatoi(optarg); + *(int*)(opt[ii].value) = xatoi(optarg); break; case OPTION_STRING: case OPTION_OPTSTRING: if (optarg) - *(char**)opt[ii].value = (char*)optarg; + *(char**)(opt[ii].value) = (char*)optarg; + break; + case OPTION_LIST: + *(GList**)(opt[ii].value) = g_list_append(*(GList**)(opt[ii].value), optarg); break; - case OPTION_GROUP: case OPTION_END: break; } diff --git a/src/lib/parse_options.h b/src/lib/parse_options.h index 105f081c..82f3c6b8 100644 --- a/src/lib/parse_options.h +++ b/src/lib/parse_options.h @@ -1,18 +1,34 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 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. +*/ #ifndef PARSE_OPTIONS_H #define PARSE_OPTIONS_H - #ifdef __cplusplus extern "C" { #endif enum parse_opt_type { OPTION_BOOL, - OPTION_GROUP, OPTION_STRING, OPTION_INTEGER, OPTION_OPTSTRING, + OPTION_LIST, OPTION_END, }; @@ -29,22 +45,24 @@ struct options { * s - short_name * l - long_name * v - value - * a - argh argument help + * a - option parameter name (for help text) * h - help */ #define OPT_END() { OPTION_END } #define OPT_BOOL(s, l, v, h) { OPTION_BOOL, (s), (l), (v), NULL, (h) } -#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } -#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), "n", (h) } +#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), "NUM", (h) } #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } #define OPT_OPTSTRING(s, l, v, a, h) { OPTION_OPTSTRING, (s), (l), (v), (a), (h) } +#define OPT_LIST(s, l, v, a, h) { OPTION_LIST, (s), (l), (v), (a), (h) } -#define OPT__VERBOSE(v) OPT_BOOL('v', "verbose", (v), "be verbose") +#define OPT__VERBOSE(v) OPT_BOOL('v', "verbose", (v), _("Be verbose")) +#define parse_opts abrt_parse_opts unsigned parse_opts(int argc, char **argv, const struct options *opt, const char *usage); -void parse_usage_and_die(const char *usage, const struct options *opt); +#define show_usage_and_die abrt_show_usage_and_die +void show_usage_and_die(const char *usage, const struct options *opt); #ifdef __cplusplus } diff --git a/src/lib/parse_release.cpp b/src/lib/parse_release.c index f9057bfe..b24f928c 100644 --- a/src/lib/parse_release.cpp +++ b/src/lib/parse_release.c @@ -19,7 +19,7 @@ #include "abrtlib.h" // caller is reposible for freeing *product* and *version* -void parse_release(const char *release, char** product, char** version) +static void parse_release(const char *release, char** product, char** version, bool append_rhel_version) { if (strstr(release, "Rawhide")) { @@ -33,21 +33,31 @@ void parse_release(const char *release, char** product, char** version) if (strstr(release, "Fedora")) strbuf_append_str(buf_product, "Fedora"); else if (strstr(release, "Red Hat Enterprise Linux")) - strbuf_append_str(buf_product, "Red Hat Enterprise Linux "); + strbuf_append_str(buf_product, "Red Hat Enterprise Linux"); + else + { + /* TODO: add logic for parsing other distros' names here */ + strbuf_append_str(buf_product, release); + } const char *r = strstr(release, "release"); const char *space = r ? strchr(r, ' ') : NULL; struct strbuf *buf_version = strbuf_new(); - if (space++) + if (space) { + space++; while (*space != '\0' && *space != ' ') { /* Eat string like "5.2" */ strbuf_append_char(buf_version, *space); - if ((strcmp(buf_product->buf, "Red Hat Enterprise Linux ") == 0)) + if (append_rhel_version + && strcmp(buf_product->buf, "Red Hat Enterprise Linux") == 0 + ) { + strbuf_append_char(buf_product, ' '); strbuf_append_char(buf_product, *space); - + } + append_rhel_version = false; space++; } } @@ -57,3 +67,15 @@ void parse_release(const char *release, char** product, char** version) VERB3 log("%s: version:'%s' product:'%s'", __func__, *version, *product); } + +void parse_release_for_bz(const char *release, char** product, char** version) +{ + /* Fedora/RH bugzilla uses "Red Hat Enterprise Linux N" product RHEL */ + parse_release(release, product, version, /*append_rhel_version:*/ true); +} + +void parse_release_for_rhts(const char *release, char** product, char** version) +{ + /* RHTS uses "Red Hat Enterprise Linux" product for RHEL */ + parse_release(release, product, version, /*append_rhel_version:*/ false); +} diff --git a/src/lib/read_write.h b/src/lib/read_write.h index 054a1a9a..dc85f33b 100644 --- a/src/lib/read_write.h +++ b/src/lib/read_write.h @@ -32,14 +32,20 @@ extern "C" { // NB: will return short read on error, not -1, // if some data was read before error occurred +#define xread abrt_xread void xread(int fd, void *buf, size_t count); +#define safe_read abrt_safe_read ssize_t safe_read(int fd, void *buf, size_t count); +#define safe_write abrt_safe_write ssize_t safe_write(int fd, const void *buf, size_t count); +#define full_read abrt_full_read ssize_t full_read(int fd, void *buf, size_t count); +#define full_write abrt_full_write ssize_t full_write(int fd, const void *buf, size_t count); +#define full_write_str abrt_full_write_str ssize_t full_write_str(int fd, const char *buf); #ifdef __cplusplus diff --git a/src/lib/run_event.c b/src/lib/run_event.c index 4ff1070d..e96e762f 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -16,36 +16,78 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <glob.h> #include "abrtlib.h" -int run_event(struct run_event_state *state, +struct run_event_state *new_run_event_state() +{ + return xzalloc(sizeof(struct run_event_state)); +} + +void free_run_event_state(struct run_event_state *state) +{ + if (state) + { + free_commands(state); + free(state); + } +} + + +/* Asyncronous command execution */ + +/* It is not yet clear whether we need to re-parse event config file + * and re-check the elements in dump dir after each comamnd. + * + * Consider this config file: + * + * EVENT=e cmd1 + * EVENT=e foo=bar cmd2 + * EVENT=e foo=baz cmd3 + * + * Imagine that element foo existed and was equal to bar at the beginning. + * After cmd1, should we execute cmd2 if element foo disappeared? + * After cmd1/2, should we execute cmd3 if element foo changed value to baz? + * + * So far, we read entire config file and select a list of commands to execute, + * checking all conditions in the beginning. It is a bit more simple to code up. + * But we may want to change it later. Therefore list of commands machinery + * is encapsulated in struct run_event_state and public async API: + * prepare_commands(state, dir, event); + * spawn_next_command(state, dir, event); + * free_commands(state); + * does not expose it. + */ + +static GList *load_event_config(GList *list, const char *dump_dir_name, - const char *event + const char *event, + const char *conf_file_name ) { - FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r"); + FILE *conffile = fopen(conf_file_name, "r"); if (!conffile) { - error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf"); - return 1; + error_msg("Can't open '%s'", conf_file_name); + return list; } - close_on_exec_on(fileno(conffile)); - - /* Export some useful environment variables for children */ - /* 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); - free(full_name); - setenv("EVENT", event, 1); - /* Read, match, and execute lines from abrt_event.conf */ - int retval = -1; + /* Read, match, and remember commands to execute */ struct dump_dir *dd = NULL; - char *line; - while ((line = xmalloc_fgetline(conffile)) != NULL) + char *next_line = xmalloc_fgetline(conffile); + while (next_line) { + char *line = next_line; + while (1) + { + next_line = xmalloc_fgetline(conffile); + if (!next_line || !isblank(next_line[0])) + break; + char *old_line = line; + line = xasprintf("%s\n%s", line, next_line); + free(old_line); + free(next_line); + } + /* Line has form: [VAR=VAL]... PROG [ARGS] */ char *p = skip_whitespace(line); if (*p == '\0' || *p == '#') @@ -53,6 +95,43 @@ int run_event(struct run_event_state *state, VERB3 log("%s: line '%s'", __func__, p); + if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")])) + { + /* include GLOB_PATTERN */ + p = skip_whitespace(p + strlen("include")); + + const char *last_slash; + char *name_to_glob; + if (*p != '/' + && (last_slash = strrchr(conf_file_name, '/')) != NULL + ) + /* GLOB_PATTERN is relative, and this include is in path/to/file.conf + * Construct path/to/GLOB_PATTERN: + */ + name_to_glob = xasprintf("%.*s%s", (int)(last_slash - conf_file_name + 1), conf_file_name, p); + else + /* Either GLOB_PATTERN is absolute, or this include is in file.conf + * (no slashes in its name). Use unchanged GLOB_PATTERN: + */ + name_to_glob = xstrdup(p); + + glob_t globbuf; + memset(&globbuf, 0, sizeof(globbuf)); + 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); + list = load_event_config(list, dump_dir_name, event, *name); + VERB3 log("%s: returned from '%s'", __func__, *name); + name++; + } + globfree(&globbuf); + goto next_line; + } + while (1) /* word loop */ { char *end_word = skip_non_whitespace(p); @@ -83,7 +162,11 @@ int run_event(struct run_event_state *state, { dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) + { + free(line); + free(next_line); goto stop; /* error (note: dd_opendir logged error msg) */ + } } real_val = malloced_val = dd_load_text_ext(dd, p, DD_FAIL_QUIETLY); } @@ -104,54 +187,133 @@ int run_event(struct run_event_state *state, p = next_word; } /* end of word loop */ - /* Don't keep dump dir locked across program runs */ - dd_close(dd); - dd = NULL; + /* We found matching line, remember its command */ + VERB1 log("Adding '%s'", p); + overlapping_strcpy(line, p); + list = g_list_append(list, line); + continue; + + next_line: + free(line); + } /* end of line loop */ + + stop: + dd_close(dd); + fclose(conffile); + + return list; +} + +int prepare_commands(struct run_event_state *state, + const char *dump_dir_name, + const char *event +) { + free_commands(state); - /* We found matching line, execute its command(s) in shell */ + state->children_count = 0; + + GList *commands = load_event_config(NULL, dump_dir_name, event, CONF_DIR"/abrt_event.conf"); + state->commands = commands; + return commands != NULL; +} + +void free_commands(struct run_event_state *state) +{ + list_free_with_free(state->commands); + state->commands = NULL; + state->command_out_fd = -1; + state->command_pid = 0; +} + +/* event parameter is unused for now, + * but may be needed if we change implementation later + */ +int spawn_next_command(struct run_event_state *state, + const char *dump_dir_name, + const char *event +) { + if (!state->commands) + return -1; + + /* We count it even if fork fails. The counter isn't meant + * to count *successful* forks, it is meant to let caller know + * whether the event we run has *any* handlers configured, or not. + */ + state->children_count++; + + char *cmd = state->commands->data; + VERB1 log("Executing '%s'", cmd); + + /* Export some useful environment variables for children */ + /* 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); + free(full_name); + setenv("EVENT", event, 1); +//FIXME: set vars in the child, not here! Need to improve fork_execv_on_steroids... + + char *argv[4]; + argv[0] = (char*)"/bin/sh"; + argv[1] = (char*)"-c"; + argv[2] = cmd; + argv[3] = NULL; + + int pipefds[2]; + state->command_pid = fork_execv_on_steroids( + EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + argv, + pipefds, + /* unsetenv_vec: */ NULL, + /* dir: */ dump_dir_name, + /* uid(unused): */ 0 + ); + state->command_out_fd = pipefds[0]; + + state->commands = g_list_remove(state->commands, cmd); + + return 0; +} + + +/* Syncronous command execution: + */ +int run_event_on_dir_name(struct run_event_state *state, + const char *dump_dir_name, + const char *event +) { + prepare_commands(state, dump_dir_name, event); + + /* Execute every command in shell */ + + int retval = 0; + while (spawn_next_command(state, dump_dir_name, event) >= 0) + { + /* Consume log from stdout */ + FILE *fp = fdopen(state->command_out_fd, "r"); + if (!fp) + die_out_of_memory(); + char *buf; + while ((buf = xmalloc_fgetline(fp)) != NULL) { - VERB1 log("Executing '%s'", p); - - /* /bin/sh -c 'cmd [args]' NULL */ - char *argv[4]; - char **pp = argv; - *pp++ = (char*)"/bin/sh"; - *pp++ = (char*)"-c"; - *pp++ = (char*)p; - *pp = NULL; - int pipefds[2]; - pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, - argv, - pipefds, - /* unsetenv_vec: */ NULL, - /* dir: */ dump_dir_name, - /* uid(unused): */ 0 - ); - free(line); - line = NULL; - - /* Consume log from stdout */ - FILE *fp = fdopen(pipefds[0], "r"); - if (!fp) - die_out_of_memory(); - char *buf; - while ((buf = xmalloc_fgetline(fp)) != NULL) - { - if (state->logging_callback) - buf = state->logging_callback(buf, state->logging_param); - free(buf); - } - fclose(fp); /* Got EOF, close. This also closes pipefds[0] */ + if (state->logging_callback) + buf = state->logging_callback(buf, state->logging_param); + free(buf); + } + fclose(fp); /* Got EOF, close. This also closes state->command_out_fd */ - /* Wait for child to actually exit, collect status */ - int status; - waitpid(pid, &status, 0); + /* Wait for child to actually exit, collect status */ + int status; + waitpid(state->command_pid, &status, 0); - retval = WEXITSTATUS(status); - if (WIFSIGNALED(status)) - retval = WTERMSIG(status) + 128; - if (retval != 0) - break; + retval = WEXITSTATUS(status); + if (WIFSIGNALED(status)) + retval = WTERMSIG(status) + 128; + if (retval != 0) + { + break; } if (state->post_run_callback) @@ -160,26 +322,49 @@ int run_event(struct run_event_state *state, if (retval != 0) break; } + } - next_line: - free(line); - } /* end of line loop */ - - stop: - free(line); - dd_close(dd); - fclose(conffile); + free_commands(state); return retval; } -char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx) +int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char *event) { - FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r"); + state->children_count = 0; + + struct dump_dir *dd = create_dump_dir_from_crash_data(data, NULL); + if (!dd) + return -1; + char *dir_name = xstrdup(dd->dd_dirname); + dd_close(dd); + + int r = run_event_on_dir_name(state, dir_name, event); + + g_hash_table_remove_all(data); + dd = dd_opendir(dir_name, 0); + free(dir_name); + if (dd) + { + load_crash_data_from_dump_dir(data, dd); + dd_delete(dd); + } + return r; +} + + +/* TODO: very similar to run_event_helper, try to combine into one fn? */ +static int list_possible_events_helper(struct strbuf *result, + struct dump_dir *dd, + const char *dump_dir_name, + const char *pfx, + const char *conf_file_name +) { + FILE *conffile = fopen(conf_file_name, "r"); if (!conffile) { - error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf"); - return NULL; + error_msg("Can't open '%s'", conf_file_name); + return 0; } /* We check "dump_dir_name == NULL" later. @@ -189,11 +374,23 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const if (dd) dump_dir_name = NULL; + int error = 0; unsigned pfx_len = strlen(pfx); - struct strbuf *result = strbuf_new(); - char *line; - while ((line = xmalloc_fgetline(conffile)) != NULL) + char *next_line = xmalloc_fgetline(conffile); + while (next_line) { + char *line = next_line; + while (1) + { + next_line = xmalloc_fgetline(conffile); + if (!next_line || !isblank(next_line[0])) + break; + char *old_line = line; + line = xasprintf("%s\n%s", line, next_line); + free(old_line); + free(next_line); + } + /* Line has form: [VAR=VAL]... PROG [ARGS] */ char *p = skip_whitespace(line); if (*p == '\0' || *p == '#') @@ -201,6 +398,45 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const VERB3 log("%s: line '%s'", __func__, p); + if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")])) + { + /* include GLOB_PATTERN */ + p = skip_whitespace(p + strlen("include")); + + const char *last_slash; + char *name_to_glob; + if (*p != '/' + && (last_slash = strrchr(conf_file_name, '/')) != NULL + ) + /* GLOB_PATTERN is relative, and this include is in path/to/file.conf + * Construct path/to/GLOB_PATTERN: + */ + name_to_glob = xasprintf("%.*s%s", (int)(last_slash - conf_file_name + 1), conf_file_name, p); + else + /* Either GLOB_PATTERN is absolute, or this include is in file.conf + * (no slashes in its name). Use unchanged GLOB_PATTERN: + */ + name_to_glob = xstrdup(p); + + glob_t globbuf; + memset(&globbuf, 0, sizeof(globbuf)); + 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); + error = list_possible_events_helper(result, dd, dump_dir_name, pfx, *name); + VERB3 log("%s: returned from '%s'", __func__, *name); + if (error) + break; + name++; + } + globfree(&globbuf); + goto next_line; + } + while (1) /* word loop */ { char *end_word = skip_non_whitespace(p); @@ -233,13 +469,21 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const goto next_word; dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) + { + error = -1; + free(line); + free(next_line); goto stop; /* error (note: dd_opendir logged error msg) */ + } } char *real_val = dd_load_text_ext(dd, p, DD_FAIL_QUIETLY); /* Does VAL match? */ if (strcmp(real_val, line_val) != 0) { - VERB3 log("var '%s': '%s'!='%s', skipping line", p, 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 */ } @@ -265,10 +509,18 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const } /* end of line loop */ stop: - free(line); if (dump_dir_name != NULL) dd_close(dd); fclose(conffile); + return error; +} + +char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx) +{ + struct strbuf *result = strbuf_new(); + int error = list_possible_events_helper(result, dd, dump_dir_name, pfx, CONF_DIR"/abrt_event.conf"); + if (error) + strbuf_clear(result); return strbuf_free_nobuf(result); } diff --git a/src/lib/spawn.c b/src/lib/spawn.c index 068f4ac7..f6b7263c 100644 --- a/src/lib/spawn.c +++ b/src/lib/spawn.c @@ -108,7 +108,7 @@ pid_t fork_execv_on_steroids(int flags, execvp(argv[0], argv); if (!(flags & EXECFLG_QUIET)) perror_msg("Can't execute '%s'", argv[0]); - exit(127); /* shell uses this exitcode in this case */ + exit(127); /* shell uses this exit code in this case */ } if (flags & EXECFLG_INPUT) { diff --git a/src/lib/steal_directory.c b/src/lib/steal_directory.c new file mode 100644 index 00000000..a77861ee --- /dev/null +++ b/src/lib/steal_directory.c @@ -0,0 +1,40 @@ +#include "abrtlib.h" + +struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name) +{ + const char *base_name = strrchr(dump_dir_name, '/'); + if (base_name) + base_name++; + else + base_name = dump_dir_name; + + struct dump_dir *dd_dst; + unsigned count = 100; + char *dst_dir_name = concat_path_file(base_dir, base_name); + while (1) + { + dd_dst = dd_create(dst_dir_name, (uid_t)-1); + free(dst_dir_name); + if (dd_dst) + break; + if (--count == 0) + { + error_msg("Can't create new dump dir in '%s'", base_dir); + return NULL; + } + struct timeval tv; + gettimeofday(&tv, NULL); + dst_dir_name = xasprintf("%s/%s.%u", base_dir, base_name, (int)tv.tv_usec); + } + + VERB1 log("Creating copy in '%s'", dd_dst->dd_dirname); + if (copy_file_recursive(dump_dir_name, dd_dst->dd_dirname) < 0) + { + /* error. copy_file_recursive already emitted error message */ + /* Don't leave half-copied dir lying around */ + dd_delete(dd_dst); + return NULL; + } + + return dd_dst; +} diff --git a/src/lib/strbuf.c b/src/lib/strbuf.c index 04a35998..f56815a0 100644 --- a/src/lib/strbuf.c +++ b/src/lib/strbuf.c @@ -37,7 +37,7 @@ int suffixcmp(const char *str, const char *suffix) return strcmp(str + len_minus_suflen, suffix); } -struct strbuf *strbuf_new() +struct strbuf *strbuf_new(void) { struct strbuf *buf = xzalloc(sizeof(*buf)); /*buf->len = 0; - done by xzalloc */ diff --git a/src/lib/strbuf.h b/src/lib/strbuf.h index dc45a199..44c6599a 100644 --- a/src/lib/strbuf.h +++ b/src/lib/strbuf.h @@ -39,13 +39,15 @@ struct strbuf * It never returns NULL. The returned pointer must be released by * calling the function strbuf_free(). */ -struct strbuf *strbuf_new(); +#define strbuf_new abrt_strbuf_new +struct strbuf *strbuf_new(void); /** * Releases the memory held by the string buffer. * @param strbuf * If the strbuf is NULL, no operation is performed. */ +#define strbuf_free abrt_strbuf_free void strbuf_free(struct strbuf *strbuf); /** @@ -53,24 +55,28 @@ void strbuf_free(struct strbuf *strbuf); * string buffer is returned. Caller is responsible to release the * returned memory using free(). */ +#define strbuf_free_nobuf abrt_strbuf_free_nobuf char* strbuf_free_nobuf(struct strbuf *strbuf); /** * The string content is set to an empty string, erasing any previous * content and leaving its length at 0 characters. */ +#define strbuf_clear abrt_strbuf_clear void strbuf_clear(struct strbuf *strbuf); /** * The current content of the string buffer is extended by adding a * character c at its end. */ +#define strbuf_append_char abrt_strbuf_append_char struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c); /** * The current content of the string buffer is extended by adding a * string str at its end. */ +#define strbuf_append_str abrt_strbuf_append_str struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str); @@ -78,6 +84,7 @@ struct strbuf *strbuf_append_str(struct strbuf *strbuf, * The current content of the string buffer is extended by inserting a * string str at its beginning. */ +#define strbuf_prepend_str abrt_strbuf_prepend_str struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str); @@ -85,6 +92,7 @@ struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, * The current content of the string buffer is extended by adding a * sequence of data formatted as the format argument specifies. */ +#define strbuf_append_strf abrt_strbuf_append_strf struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...); @@ -93,6 +101,7 @@ struct strbuf *strbuf_append_strf(struct strbuf *strbuf, * sequence of data formatted as the format argument specifies at the * buffer beginning. */ +#define strbuf_prepend_strf abrt_strbuf_prepend_strf struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...); diff --git a/src/lib/stringops.cpp b/src/lib/stringops.cpp deleted file mode 100644 index 7bc5413f..00000000 --- a/src/lib/stringops.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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 "abrtlib.h" - -void parse_args(const char *psArgs, vector_string_t& pArgs, int quote) -{ - unsigned ii; - bool inside_quotes = false; - std::string item; - - for (ii = 0; psArgs[ii]; ii++) - { - if (quote != -1) - { - if (psArgs[ii] == quote) - { - inside_quotes = !inside_quotes; - continue; - } - /* inside quotes we support escaping with \x */ - if (inside_quotes && psArgs[ii] == '\\' && psArgs[ii+1]) - { - ii++; - item += psArgs[ii]; - continue; - } - } - if (psArgs[ii] == ',' && !inside_quotes) - { - pArgs.push_back(item); - item.clear(); - continue; - } - item += psArgs[ii]; - } - - if (item.size() != 0) - { - pArgs.push_back(item); - } -} diff --git a/src/lib/xatonum.c b/src/lib/xatonum.c index 3b22071f..1a92db7f 100644 --- a/src/lib/xatonum.c +++ b/src/lib/xatonum.c @@ -28,7 +28,7 @@ inval: error_msg_and_die("invalid number '%s'", numstr); } -int xatoi_u(const char *numstr) +int xatoi_positive(const char *numstr) { unsigned r = xatou(numstr); if (r > (unsigned)INT_MAX) @@ -41,7 +41,7 @@ int xatoi(const char *numstr) unsigned r; if (*numstr != '-') - return xatoi_u(numstr); + return xatoi_positive(numstr); r = xatou(numstr + 1); if (r > (unsigned)INT_MAX + 1) diff --git a/src/lib/xfuncs.c b/src/lib/xfuncs.c index d5166037..f451693a 100644 --- a/src/lib/xfuncs.c +++ b/src/lib/xfuncs.c @@ -26,12 +26,18 @@ /* Turn on nonblocking I/O on a fd */ int ndelay_on(int fd) { - return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + int flags = fcntl(fd, F_GETFL); + if (flags & O_NONBLOCK) + return 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } int ndelay_off(int fd) { - return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); + int flags = fcntl(fd, F_GETFL); + if (!(flags & O_NONBLOCK)) + return 0; + return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } int close_on_exec_on(int fd) @@ -39,16 +45,6 @@ int close_on_exec_on(int fd) return fcntl(fd, F_SETFD, FD_CLOEXEC); } -#if 0 /* unused */ -void *xcalloc(size_t nmemb, size_t size) -{ - void *ptr = calloc(nmemb, size); - if (!ptr && nmemb && size) - die_out_of_memory(); - return ptr; -} -#endif - // Die if we can't allocate size bytes of memory. void* xmalloc(size_t size) { @@ -290,7 +286,7 @@ int xopen(const char *pathname, int flags) void xunlink(const char *pathname) { if (unlink(pathname)) - perror_msg_and_die("can't remove file '%s'", pathname); + perror_msg_and_die("Can't remove file '%s'", pathname); } #if 0 //UNUSED |