summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2011-03-01 12:08:36 +0100
committerKarel Klic <kklic@redhat.com>2011-03-01 12:08:36 +0100
commit85f639b7fe277ba327e5013e5b101b4a67f14e1d (patch)
tree7caa3999e8c987e3ddbc26f4bfbbdc73defca73f /src/lib
parentfb52104af74bbf6eeda394880666df40b4354aba (diff)
parent77468fcdd7cc05db52320c373a24a5490ff32f52 (diff)
downloadabrt-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.cpp94
-rw-r--r--src/lib/Makefile.am66
-rw-r--r--src/lib/abrt_curl.c15
-rw-r--r--src/lib/abrt_dbus.c438
-rw-r--r--src/lib/abrt_dbus.h59
-rw-r--r--src/lib/abrt_types.c (renamed from src/lib/Plugin.cpp)36
-rw-r--r--src/lib/abrt_xmlrpc.cpp18
-rw-r--r--src/lib/abrt_xmlrpc.h12
-rw-r--r--src/lib/binhex.c74
-rw-r--r--src/lib/copy_file_recursive.c139
-rw-r--r--src/lib/crash_data.c (renamed from src/lib/crash_dump.cpp)187
-rw-r--r--src/lib/create_dump_dir.c85
-rw-r--r--src/lib/dump_dir.c468
-rw-r--r--src/lib/glib_support.c (renamed from src/lib/ABRTException.cpp)21
-rw-r--r--src/lib/hash_md5.h3
-rw-r--r--src/lib/hash_sha1.h3
-rw-r--r--src/lib/hooklib.c2
-rw-r--r--src/lib/hooklib.h8
-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.h29
-rw-r--r--src/lib/make_descr.c (renamed from src/lib/make_descr.cpp)136
-rw-r--r--src/lib/parse_options.c45
-rw-r--r--src/lib/parse_options.h32
-rw-r--r--src/lib/parse_release.c (renamed from src/lib/parse_release.cpp)32
-rw-r--r--src/lib/read_write.h6
-rw-r--r--src/lib/run_event.c414
-rw-r--r--src/lib/spawn.c2
-rw-r--r--src/lib/steal_directory.c40
-rw-r--r--src/lib/strbuf.c2
-rw-r--r--src/lib/strbuf.h11
-rw-r--r--src/lib/stringops.cpp57
-rw-r--r--src/lib/xatonum.c4
-rw-r--r--src/lib/xfuncs.c22
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