summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-11-15 15:29:24 +0100
committerDenys Vlasenko <dvlasenk@redhat.com>2010-11-15 15:29:24 +0100
commit2cf0d770b66b6c6ce50af2767f575db552cd784c (patch)
tree56ba70257c94add690ddaa8b0ec961c6964a146e /src/lib
parent2169959c6ba2d77512b8b39366a4d3e476931b4a (diff)
downloadabrt-2cf0d770b66b6c6ce50af2767f575db552cd784c.tar.gz
abrt-2cf0d770b66b6c6ce50af2767f575db552cd784c.tar.xz
abrt-2cf0d770b66b6c6ce50af2767f575db552cd784c.zip
move inc/ and lib/ to src/. No code changes
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ABRTException.cpp33
-rw-r--r--src/lib/CommLayerInner.cpp94
-rw-r--r--src/lib/CrashTypes.cpp254
-rw-r--r--src/lib/Makefile.am109
-rw-r--r--src/lib/Plugin.cpp116
-rw-r--r--src/lib/Polkit.cpp102
-rw-r--r--src/lib/Polkit.h42
-rw-r--r--src/lib/abrt_curl.c375
-rw-r--r--src/lib/abrt_curl.h96
-rw-r--r--src/lib/abrt_dbus.c430
-rw-r--r--src/lib/abrt_dbus.h324
-rw-r--r--src/lib/abrt_packages.c43
-rw-r--r--src/lib/abrt_packages.h33
-rw-r--r--src/lib/abrt_xmlrpc.cpp97
-rw-r--r--src/lib/abrt_xmlrpc.h48
-rw-r--r--src/lib/append_to_malloced_string.c27
-rw-r--r--src/lib/concat_path_file.c25
-rw-r--r--src/lib/copyfd.c172
-rw-r--r--src/lib/daemon.c139
-rw-r--r--src/lib/database.c73
-rw-r--r--src/lib/dirsize.c110
-rw-r--r--src/lib/dump_dir.c504
-rw-r--r--src/lib/encbase64.c78
-rw-r--r--src/lib/hash_md5.c444
-rw-r--r--src/lib/hash_md5.h29
-rw-r--r--src/lib/hash_sha1.c213
-rw-r--r--src/lib/hash_sha1.h40
-rw-r--r--src/lib/hooklib.c138
-rw-r--r--src/lib/hooklib.h29
-rw-r--r--src/lib/logging.c148
-rw-r--r--src/lib/logging.h78
-rw-r--r--src/lib/make_descr.cpp287
-rw-r--r--src/lib/numtoa.cpp34
-rw-r--r--src/lib/overlapping_strcpy.c22
-rw-r--r--src/lib/parse_options.c182
-rw-r--r--src/lib/parse_options.h53
-rw-r--r--src/lib/parse_release.cpp59
-rw-r--r--src/lib/read_write.c105
-rw-r--r--src/lib/read_write.h49
-rw-r--r--src/lib/run_event.c276
-rw-r--r--src/lib/skip_whitespace.c22
-rw-r--r--src/lib/spawn.c156
-rw-r--r--src/lib/stdio_helpers.c69
-rw-r--r--src/lib/strbuf.c147
-rw-r--r--src/lib/strbuf.h103
-rw-r--r--src/lib/stringops.cpp57
-rw-r--r--src/lib/test.cpp105
-rw-r--r--src/lib/time.c65
-rw-r--r--src/lib/xatonum.c50
-rw-r--r--src/lib/xconnect.c415
-rw-r--r--src/lib/xfuncs.c400
51 files changed, 7099 insertions, 0 deletions
diff --git a/src/lib/ABRTException.cpp b/src/lib/ABRTException.cpp
new file mode 100644
index 00000000..0ae5d452
--- /dev/null
+++ b/src/lib/ABRTException.cpp
@@ -0,0 +1,33 @@
+/*
+ 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 "abrt_exception.h"
+
+CABRTException::CABRTException(abrt_exception_t type, const char* fmt, ...)
+{
+ m_type = type;
+ va_list ap;
+ va_start(ap, fmt);
+ m_what = xvasprintf(fmt, ap);
+ va_end(ap);
+}
+
+CABRTException::CABRTException(const CABRTException& rhs):
+ m_type(rhs.m_type),
+ m_what(xstrdup(rhs.m_what))
+{}
diff --git a/src/lib/CommLayerInner.cpp b/src/lib/CommLayerInner.cpp
new file mode 100644
index 00000000..3c102d6e
--- /dev/null
+++ b/src/lib/CommLayerInner.cpp
@@ -0,0 +1,94 @@
+/*
+ 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/CrashTypes.cpp b/src/lib/CrashTypes.cpp
new file mode 100644
index 00000000..acacda5e
--- /dev/null
+++ b/src/lib/CrashTypes.cpp
@@ -0,0 +1,254 @@
+/*
+ Copyright (C) 2010 Denys Vlasenko (dvlasenk@redhat.com)
+ 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"
+#include "crash_types.h"
+
+const char *const must_have_files[] = {
+ FILENAME_ARCHITECTURE,
+ FILENAME_KERNEL ,
+ FILENAME_PACKAGE ,
+ FILENAME_COMPONENT ,
+ FILENAME_RELEASE ,
+ FILENAME_EXECUTABLE ,
+ NULL
+};
+
+static const char *const editable_files[] = {
+ FILENAME_DESCRIPTION,
+ FILENAME_COMMENT ,
+ FILENAME_REPRODUCE ,
+ FILENAME_BACKTRACE ,
+ NULL
+};
+
+static bool is_editable(const char *name, const char *const *v)
+{
+ while (*v) {
+ if (strcmp(*v, name) == 0)
+ return true;
+ v++;
+ }
+ return false;
+}
+
+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
+ * that file containing just "0" is not text (!!)
+ * So, we do it ourself.
+ */
+
+ int fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return NULL; /* it's not text (because it does not exist! :) */
+
+ /* Maybe 64k limit is small. But _some_ limit is necessary:
+ * fields declared "text" may end up in editing fields and such.
+ * We don't want to accidentally end up with 100meg text in a textbox!
+ * So, don't remove this. If you really need to, raise the limit.
+ */
+ off_t size = lseek(fd, 0, SEEK_END);
+ if (size < 0 || size > 64*1024)
+ {
+ close(fd);
+ return NULL; /* it's not a SMALL text */
+ }
+ lseek(fd, 0, SEEK_SET);
+
+ char *buf = (char*)xmalloc(*sz);
+ ssize_t r = *sz = full_read(fd, buf, *sz);
+ close(fd);
+ if (r < 0)
+ {
+ free(buf);
+ return NULL; /* it's not text (because we can't read it) */
+ }
+
+ /* Some files in our dump directories are known to always be textual */
+ const char *base = strrchr(name, '/');
+ if (base)
+ {
+ base++;
+ if (strcmp(base, FILENAME_BACKTRACE) == 0
+ || strcmp(base, FILENAME_CMDLINE) == 0
+ ) {
+ return buf;
+ }
+ }
+
+ /* Every once in a while, even a text file contains a few garbled
+ * or unexpected non-ASCII chars. We should not declare it "binary".
+ */
+ const unsigned RATIO = 50;
+ unsigned total_chars = r + RATIO;
+ unsigned bad_chars = 1; /* 1 prevents division by 0 later */
+ while (--r >= 0)
+ {
+ if (buf[r] >= 0x7f
+ /* among control chars, only '\t','\n' etc are allowed */
+ || (buf[r] < ' ' && !isspace(buf[r]))
+ ) {
+ if (buf[r] == '\0')
+ {
+ /* We don't like NULs very much. Not text for sure! */
+ free(buf);
+ return NULL;
+ }
+ bad_chars++;
+ }
+ }
+
+ if ((total_chars / bad_chars) >= RATIO)
+ return buf; /* looks like text to me */
+
+ free(buf);
+ return NULL; /* it's binary */
+}
+
+void load_crash_data_from_debug_dump(struct dump_dir *dd, map_crash_data_t& data)
+{
+ char *short_name;
+ char *full_name;
+
+ dd_init_next_file(dd);
+ while (dd_get_next_file(dd, &short_name, &full_name))
+ {
+ ssize_t sz = 4*1024;
+ char *text = NULL;
+ bool editable = is_editable_file(short_name);
+
+ if (!editable)
+ {
+ text = is_text_file(full_name, &sz);
+ if (!text)
+ {
+ add_to_crash_data_ext(data,
+ short_name,
+ CD_BIN,
+ CD_ISNOTEDITABLE,
+ full_name
+ );
+
+ free(short_name);
+ free(full_name);
+ continue;
+ }
+ }
+
+ char *content;
+ if (sz < 4*1024) /* is_text_file did read entire file */
+ content = xstrndup(text, sz); //TODO: can avoid this copying if is_text_file() adds NUL
+ else /* no, need to read it all */
+ content = dd_load_text(dd, short_name);
+ free(text);
+
+ add_to_crash_data_ext(data,
+ short_name,
+ CD_TXT,
+ editable ? CD_ISEDITABLE : CD_ISNOTEDITABLE,
+ content
+ );
+ free(short_name);
+ free(full_name);
+ free(content);
+ }
+}
+
+static const std::string* helper_get_crash_data_item_content(const map_crash_data_t& crash_data, const char *key)
+{
+ 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];
+}
+
+const std::string& get_crash_data_item_content(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 == 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++;
+ }
+}
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644
index 00000000..153af2d0
--- /dev/null
+++ b/src/lib/Makefile.am
@@ -0,0 +1,109 @@
+# ABRTUtils has small set of deps. This reduces deps of smaller abrt binaries
+# ABRTdUtils has much more. It is used by daemon and plugins only
+# ABRT_web_utils is used only by some abrt-action-foo programs, not by daemon
+lib_LTLIBRARIES = \
+ libABRTUtils.la \
+ libABRTdUtils.la \
+ libABRT_web_utils.la
+HEADER_DIR = $(srcdir)/../include
+AM_CPPFLAGS = -I$(HEADER_DIR)
+
+# Not used just yet:
+# time.cpp
+# xconnect.cpp
+
+libABRTUtils_la_SOURCES = \
+ xfuncs.c \
+ concat_path_file.c \
+ append_to_malloced_string.c \
+ overlapping_strcpy.c \
+ encbase64.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 \
+ daemon.c \
+ skip_whitespace.c \
+ xatonum.c numtoa.cpp \
+ spawn.c \
+ stringops.cpp \
+ dirsize.c \
+ dump_dir.c \
+ strbuf.c strbuf.h \
+ abrt_dbus.c abrt_dbus.h \
+ CrashTypes.cpp \
+ ABRTException.cpp \
+ run_event.c \
+ abrt_packages.c abrt_packages.h \
+ hooklib.c hooklib.h \
+ database.c \
+ parse_options.c parse_options.h
+libABRTUtils_la_CPPFLAGS = \
+ -Wall -Werror \
+ -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) \
+ $(DBUS_CFLAGS) \
+ -D_GNU_SOURCE
+libABRTUtils_la_LDFLAGS = \
+ -version-info 0:1:0
+libABRTUtils_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(DBUS_LIBS) \
+ -ldl
+
+libABRTdUtils_la_SOURCES = \
+ parse_release.cpp \
+ make_descr.cpp \
+ $(HEADER_DIR)/comm_layer_inner.h CommLayerInner.cpp \
+ $(HEADER_DIR)/plugin.h Plugin.cpp \
+ Polkit.h Polkit.cpp
+libABRTdUtils_la_CPPFLAGS = \
+ -Wall -Werror \
+ -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)\" \
+ $(POLKIT_CFLAGS) \
+ -D_GNU_SOURCE
+libABRTdUtils_la_LDFLAGS = \
+ -version-info 0:1:0
+libABRTdUtils_la_LIBADD = \
+ $(POLKIT_LIBS) \
+ -ldl
+
+libABRT_web_utils_la_SOURCES = \
+ abrt_curl.h abrt_curl.c \
+ abrt_xmlrpc.h abrt_xmlrpc.cpp
+libABRT_web_utils_la_CPPFLAGS = \
+ -Wall -Werror \
+ -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)\" \
+ $(CURL_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
+ -D_GNU_SOURCE
+libABRT_web_utils_la_LDFLAGS = \
+ -version-info 0:1:0
+libABRT_web_utils_la_LIBADD = \
+ $(CURL_LIBS) \
+ $(LIBXML_LIBS) \
+ $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS)
+
+$(DESTDIR)/$(DEBUG_DUMPS_DIR):
+ $(mkdir_p) '$@'
+# no need to chmod it here
+#chmod 1777 '$@'
+install-data-local: $(DESTDIR)/$(DEBUG_DUMPS_DIR)
diff --git a/src/lib/Plugin.cpp b/src/lib/Plugin.cpp
new file mode 100644
index 00000000..bf237959
--- /dev/null
+++ b/src/lib/Plugin.cpp
@@ -0,0 +1,116 @@
+/*
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 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 "plugin.h"
+#include "abrtlib.h"
+
+CPlugin::CPlugin() {}
+
+/* class CPlugin's virtuals */
+CPlugin::~CPlugin() {}
+void CPlugin::Init() {}
+void CPlugin::DeInit() {}
+void CPlugin::SetSettings(const map_plugin_settings_t& pSettings)
+{
+ 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++;
+ }
+ }
+}
+
+bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings,
+ bool skipKeysWithoutValue /*= true*/)
+{
+ FILE *fp = stdin;
+ if (strcmp(pPath, "-") != 0)
+ {
+ fp = fopen(pPath, "r");
+ if (!fp)
+ return false;
+ }
+
+ char *line;
+ 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;
+ for (ii = 0; line[ii] != '\0'; ii++)
+ {
+ if (line[ii] == '"')
+ {
+ in_quote = !in_quote;
+ }
+ if (isspace(line[ii]) && !in_quote)
+ {
+ continue;
+ }
+ if (line[ii] == '#' && !in_quote && key == "")
+ {
+ break;
+ }
+ if (line[ii] == '=' && !in_quote)
+ {
+ is_value = true;
+ valid = true;
+ continue;
+ }
+ if (!is_value)
+ {
+ key += line[ii];
+ }
+ else
+ {
+ value += line[ii];
+ }
+ }
+
+ /* Skip broken or empty lines. */
+ if (!valid)
+ goto free_line;
+
+ /* Skip lines with empty key. */
+ if (key.length() == 0)
+ goto free_line;
+
+ if (skipKeysWithoutValue && value.length() == 0)
+ goto free_line;
+
+ /* Skip lines with unclosed quotes. */
+ if (in_quote)
+ goto free_line;
+
+ pSettings[key] = value;
+ free_line:
+ free(line);
+ }
+
+ if (fp != stdin)
+ fclose(fp);
+ return true;
+}
diff --git a/src/lib/Polkit.cpp b/src/lib/Polkit.cpp
new file mode 100644
index 00000000..a5e07760
--- /dev/null
+++ b/src/lib/Polkit.cpp
@@ -0,0 +1,102 @@
+/*
+ Polkit.cpp - PolicyKit integration for ABRT
+
+ Copyright (C) 2009 Daniel Novotny (dnovotny@redhat.com)
+ Copyright (C) 2009 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 <polkit/polkit.h>
+#include <glib-object.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "Polkit.h"
+#include "abrtlib.h"
+
+/*number of seconds: timeout for the authorization*/
+#define POLKIT_TIMEOUT 20
+
+static gboolean do_cancel(GCancellable* cancellable)
+{
+ log("Timer has expired; cancelling authorization check\n");
+ g_cancellable_cancel(cancellable);
+ return FALSE;
+}
+
+
+static PolkitResult do_check(PolkitSubject *subject, const char *action_id)
+{
+ PolkitAuthority *authority;
+ PolkitAuthorizationResult *result;
+ GError *error = NULL;
+ GCancellable * cancellable;
+
+ authority = polkit_authority_get();
+ cancellable = g_cancellable_new();
+
+ guint cancel_timeout = g_timeout_add(POLKIT_TIMEOUT * 1000,
+ (GSourceFunc) do_cancel,
+ cancellable);
+
+ result = polkit_authority_check_authorization_sync(authority,
+ subject,
+ action_id,
+ NULL,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+ cancellable,
+ &error);
+ g_object_unref(authority);
+ g_source_remove(cancel_timeout);
+ if (error)
+ {
+ g_error_free(error);
+ return PolkitUnknown;
+ }
+
+ if (result)
+ {
+ if (polkit_authorization_result_get_is_challenge(result))
+ {
+ /* Can't happen (happens only with
+ * POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE flag) */
+ g_object_unref(result);
+ return PolkitChallenge;
+ }
+ if (polkit_authorization_result_get_is_authorized(result))
+ {
+ g_object_unref(result);
+ return PolkitYes;
+ }
+ g_object_unref(result);
+ return PolkitNo;
+ }
+
+ return PolkitUnknown;
+}
+
+PolkitResult polkit_check_authorization(const char *dbus_name, const char *action_id)
+{
+ g_type_init();
+ PolkitSubject *subject = polkit_system_bus_name_new(dbus_name);
+ return do_check(subject, action_id);
+}
+
+PolkitResult polkit_check_authorization(pid_t pid, const char *action_id)
+{
+ g_type_init();
+ PolkitSubject *subject = polkit_unix_process_new(pid);
+ return do_check(subject, action_id);
+}
diff --git a/src/lib/Polkit.h b/src/lib/Polkit.h
new file mode 100644
index 00000000..d9e097ac
--- /dev/null
+++ b/src/lib/Polkit.h
@@ -0,0 +1,42 @@
+/*
+ Polkit.h - header file for PolicyKit integration
+
+ Copyright (C) 2009 Daniel Novotny (dnovotny@redhat.com)
+ Copyright (C) 2009 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 ABRT_POLKIT_H
+#define ABRT_POLKIT_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+typedef enum {
+/* Authorization status is unknown */
+ PolkitUnknown = 0x0,
+ /* Subject is authorized for the action */
+ PolkitYes = 0x01,
+ /* Subject is not authorized for the action */
+ PolkitNo = 0x02,
+ /* Challenge is needed for this action, only when flag is
+ * POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE */
+ PolkitChallenge = 0x03
+} PolkitResult;
+
+PolkitResult polkit_check_authorization(const char *dbus_name, const char *action_id);
+PolkitResult polkit_check_authorization(pid_t pid, const char *action_id);
+
+#endif
diff --git a/src/lib/abrt_curl.c b/src/lib/abrt_curl.c
new file mode 100644
index 00000000..f23d3949
--- /dev/null
+++ b/src/lib/abrt_curl.c
@@ -0,0 +1,375 @@
+/*
+ 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"
+#include "abrt_curl.h"
+
+/*
+ * Utility functions
+ */
+CURL* xcurl_easy_init()
+{
+ CURL* curl = curl_easy_init();
+ if (!curl)
+ {
+ error_msg_and_die("Can't create curl handle");
+ }
+ return curl;
+}
+
+static char*
+check_curl_error(CURLcode err, const char* msg)
+{
+ if (err)
+ return xasprintf("%s: %s", msg, curl_easy_strerror(err));
+ return NULL;
+}
+
+static void
+die_if_curl_error(CURLcode err)
+{
+ if (err) {
+ char *msg = check_curl_error(err, "curl");
+ error_msg_and_die("%s", msg);
+ }
+}
+
+static void
+xcurl_easy_setopt_ptr(CURL *handle, CURLoption option, const void *parameter)
+{
+ CURLcode err = curl_easy_setopt(handle, option, parameter);
+ if (err) {
+ char *msg = check_curl_error(err, "curl");
+ error_msg_and_die("%s", msg);
+ }
+}
+static inline void
+xcurl_easy_setopt_long(CURL *handle, CURLoption option, long parameter)
+{
+ xcurl_easy_setopt_ptr(handle, option, (void*)parameter);
+}
+
+/*
+ * post_state utility functions
+ */
+
+abrt_post_state_t *new_abrt_post_state(int flags)
+{
+ abrt_post_state_t *state = (abrt_post_state_t *)xzalloc(sizeof(*state));
+ state->flags = flags;
+ return state;
+}
+
+void free_abrt_post_state(abrt_post_state_t *state)
+{
+ char **headers = state->headers;
+ if (headers)
+ {
+ while (*headers)
+ free(*headers++);
+ free(state->headers);
+ }
+ free(state->curl_error_msg);
+ free(state->body);
+ free(state);
+}
+
+char *find_header_in_abrt_post_state(abrt_post_state_t *state, const char *str)
+{
+ char **headers = state->headers;
+ if (headers)
+ {
+ unsigned len = strlen(str);
+ while (*headers)
+ {
+ if (strncmp(*headers, str, len) == 0)
+ return skip_whitespace(*headers + len);
+ headers++;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * abrt_post: perform HTTP POST transaction
+ */
+
+/* "save headers" callback */
+static size_t
+save_headers(void *buffer_pv, size_t count, size_t nmemb, void *ptr)
+{
+ abrt_post_state_t* state = (abrt_post_state_t*)ptr;
+ size_t size = count * nmemb;
+
+ char *h = xstrndup((char*)buffer_pv, size);
+ strchrnul(h, '\r')[0] = '\0';
+ strchrnul(h, '\n')[0] = '\0';
+
+ unsigned cnt = state->header_cnt;
+
+ /* Check for the case when curl follows a redirect:
+ * header 0: 'HTTP/1.1 301 Moved Permanently'
+ * header 1: 'Connection: close'
+ * header 2: 'Location: NEW_URL'
+ * header 3: ''
+ * header 0: 'HTTP/1.1 200 OK' <-- we need to forget all hdrs and start anew
+ */
+ if (cnt != 0
+ && strncmp(h, "HTTP/", 5) == 0
+ && state->headers[cnt-1][0] == '\0' /* prev header is an empty string */
+ ) {
+ char **headers = state->headers;
+ if (headers)
+ {
+ while (*headers)
+ free(*headers++);
+ }
+ cnt = 0;
+ }
+
+ VERB3 log("save_headers: header %d: '%s'", cnt, h);
+ state->headers = (char**)xrealloc(state->headers, (cnt+2) * sizeof(state->headers[0]));
+ state->headers[cnt] = h;
+ state->header_cnt = ++cnt;
+ state->headers[cnt] = NULL;
+
+ return size;
+}
+
+/* "read local data from a file" callback */
+static size_t fread_with_reporting(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ static time_t last_t; // hack
+
+ FILE *fp = (FILE*)userdata;
+ time_t t = time(NULL);
+
+ // Report current file position every 16 seconds
+ if (!(t & 0xf) && last_t != t)
+ {
+ last_t = t;
+ off_t cur_pos = ftello(fp);
+ fseeko(fp, 0, SEEK_END);
+ off_t sz = ftello(fp);
+ fseeko(fp, cur_pos, SEEK_SET);
+ log(_("Uploaded: %llu of %llu kbytes"),
+ (unsigned long long)cur_pos / 1024,
+ (unsigned long long)sz / 1024);
+ }
+
+ return fread(ptr, size, nmemb, fp);
+}
+
+int
+abrt_post(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char *data,
+ off_t data_size)
+{
+ CURLcode curl_err;
+ long response_code;
+ abrt_post_state_t localstate;
+
+ VERB3 log("abrt_post('%s','%s')", url, data);
+
+ if (!state)
+ {
+ memset(&localstate, 0, sizeof(localstate));
+ state = &localstate;
+ }
+
+ state->http_resp_code = response_code = -1;
+
+ CURL *handle = xcurl_easy_init();
+
+ // Buffer[CURL_ERROR_SIZE] curl stores human readable error messages in.
+ // This may be more helpful than just return code from curl_easy_perform.
+ // curl will need it until curl_easy_cleanup.
+ state->errmsg[0] = '\0';
+ xcurl_easy_setopt_ptr(handle, CURLOPT_ERRORBUFFER, state->errmsg);
+ // "Display a lot of verbose information about its operations.
+ // Very useful for libcurl and/or protocol debugging and understanding.
+ // The verbose information will be sent to stderr, or the stream set
+ // with CURLOPT_STDERR"
+ //xcurl_easy_setopt_long(handle, CURLOPT_VERBOSE, 1);
+ // Shut off the built-in progress meter completely
+ xcurl_easy_setopt_long(handle, CURLOPT_NOPROGRESS, 1);
+
+ // TODO: do we need to check for CURLE_URL_MALFORMAT error *here*,
+ // not in curl_easy_perform?
+ xcurl_easy_setopt_ptr(handle, CURLOPT_URL, url);
+
+ // Auth if configured
+ if (state->username) {
+ // bitmask of allowed auth methods
+ xcurl_easy_setopt_long(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_USERNAME, state->username);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_PASSWORD, (state->password ? state->password : ""));
+ }
+
+ // Do a regular HTTP post. This also makes curl use
+ // a "Content-Type: application/x-www-form-urlencoded" header.
+ // (This is by far the most commonly used POST method).
+ xcurl_easy_setopt_long(handle, CURLOPT_POST, 1);
+ // Supply POST data...
+ struct curl_httppost* post = NULL;
+ struct curl_httppost* last = NULL;
+ FILE* data_file = NULL;
+ if (data_size == ABRT_POST_DATA_FROMFILE) {
+ // ...from a file
+ data_file = fopen(data, "r");
+ if (!data_file)
+//FIXME:
+ perror_msg_and_die("can't open '%s'", data);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_READDATA, data_file);
+ // Want to use custom read function
+ xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting);
+ } else if (data_size == ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) {
+ // ...from a file, in multipart/formdata format
+ const char *basename = strrchr(data, '/');
+ if (basename) basename++;
+ else basename = data;
+#if 0
+ // Simple way, without custom reader function
+ CURLFORMcode curlform_err = curl_formadd(&post, &last,
+ CURLFORM_PTRNAME, "file", // element name
+ CURLFORM_FILE, data, // filename to read from
+ CURLFORM_CONTENTTYPE, content_type,
+ CURLFORM_FILENAME, basename, // filename to put in the form
+ CURLFORM_END);
+#else
+ data_file = fopen(data, "r");
+ if (!data_file)
+//FIXME:
+ perror_msg_and_die("can't open '%s'", data);
+ // Want to use custom read function
+ xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting);
+ // Need to know file size
+ fseeko(data_file, 0, SEEK_END);
+ off_t sz = ftello(data_file);
+ fseeko(data_file, 0, SEEK_SET);
+ // Create formdata
+ CURLFORMcode curlform_err = curl_formadd(&post, &last,
+ CURLFORM_PTRNAME, "file", // element name
+ // use CURLOPT_READFUNCTION for reading, pass data_file as its last param:
+ CURLFORM_STREAM, data_file,
+ CURLFORM_CONTENTSLENGTH, (long)sz, // a must if we use CURLFORM_STREAM option
+ CURLFORM_CONTENTTYPE, content_type,
+ CURLFORM_FILENAME, basename, // filename to put in the form
+ CURLFORM_END);
+#endif
+ if (curlform_err != 0)
+//FIXME:
+ error_msg_and_die("out of memory or read error (curl_formadd error code: %d)", (int)curlform_err);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPPOST, post);
+ } else {
+ // .. from a blob in memory
+ xcurl_easy_setopt_ptr(handle, CURLOPT_POSTFIELDS, data);
+ // note1: if data_size == ABRT_POST_DATA_STRING == -1, curl will use strlen(data)
+ xcurl_easy_setopt_long(handle, CURLOPT_POSTFIELDSIZE, data_size);
+ // note2: CURLOPT_POSTFIELDSIZE_LARGE can't be used: xcurl_easy_setopt_long()
+ // 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;
+ if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA)
+ {
+ char *content_type_header = xasprintf("Content-Type: %s", content_type);
+ // Note: curl_slist_append() copies content_type_header
+ httpheader_list = curl_slist_append(httpheader_list, content_type_header);
+ if (!httpheader_list)
+ error_msg_and_die("out of memory");
+ free(content_type_header);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list);
+ }
+
+// 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,
+// but it still did not work.
+#if 0
+ // Please handle 301/302 redirects for me
+ xcurl_easy_setopt_long(handle, CURLOPT_FOLLOWLOCATION, 1);
+ xcurl_easy_setopt_long(handle, CURLOPT_MAXREDIRS, 10);
+ // Bitmask to control how libcurl acts on redirects after POSTs.
+ // Bit 0 set (value CURL_REDIR_POST_301) makes libcurl
+ // not convert POST requests into GET requests when following
+ // a 301 redirection. Bit 1 (value CURL_REDIR_POST_302) makes libcurl
+ // maintain the request method after a 302 redirect.
+ // CURL_REDIR_POST_ALL is a convenience define that sets both bits.
+ // The non-RFC behaviour is ubiquitous in web browsers, so the library
+ // does the conversion by default to maintain consistency.
+ // However, a server may require a POST to remain a POST.
+ xcurl_easy_setopt_long(handle, CURLOPT_POSTREDIR, -1L /*CURL_REDIR_POST_ALL*/ );
+#endif
+
+ // Prepare for saving information
+ if (state->flags & ABRT_POST_WANT_HEADERS)
+ {
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HEADERFUNCTION, (void*)save_headers);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEHEADER, state);
+ }
+ FILE* body_stream = NULL;
+ if (state->flags & ABRT_POST_WANT_BODY)
+ {
+ body_stream = open_memstream(&state->body, &state->body_size);
+ if (!body_stream)
+ error_msg_and_die("out of memory");
+ xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEDATA, body_stream);
+ }
+ if (!(state->flags & ABRT_POST_WANT_SSL_VERIFY))
+ {
+ xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYPEER, 0);
+ xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYHOST, 0);
+ }
+
+ // This is the place where everything happens.
+ // Here errors are not limited to "out of memory", can't just die.
+ curl_err = curl_easy_perform(handle);
+ if (curl_err)
+ {
+ VERB2 log("curl_easy_perform: error %d", (int)curl_err);
+ if (state->flags & ABRT_POST_WANT_ERROR_MSG)
+ {
+ state->curl_error_msg = check_curl_error(curl_err, "curl_easy_perform");
+ VERB3 log("curl_easy_perform: error_msg: %s", state->curl_error_msg);
+ }
+ goto ret;
+ }
+
+ // 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);
+ state->http_resp_code = response_code;
+ VERB3 log("after curl_easy_perform: http code %ld body:'%s'", response_code, state->body);
+
+ ret:
+ curl_easy_cleanup(handle);
+ if (httpheader_list)
+ curl_slist_free_all(httpheader_list);
+ if (body_stream)
+ fclose(body_stream);
+ if (data_file)
+ fclose(data_file);
+ if (post)
+ curl_formfree(post);
+
+ return response_code;
+}
diff --git a/src/lib/abrt_curl.h b/src/lib/abrt_curl.h
new file mode 100644
index 00000000..77b4605c
--- /dev/null
+++ b/src/lib/abrt_curl.h
@@ -0,0 +1,96 @@
+/*
+ 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 ABRT_CURL_H_
+#define ABRT_CURL_H_
+
+#include <curl/curl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CURL* xcurl_easy_init();
+
+typedef struct abrt_post_state {
+ /* Supplied by caller: */
+ int flags;
+ const char *username;
+ const char *password;
+ /* Results of POST transaction: */
+ int http_resp_code;
+ unsigned header_cnt;
+ char **headers;
+ char *curl_error_msg;
+ char *body;
+ size_t body_size;
+ char errmsg[CURL_ERROR_SIZE];
+} abrt_post_state_t;
+
+abrt_post_state_t *new_abrt_post_state(int flags);
+void free_abrt_post_state(abrt_post_state_t *state);
+char *find_header_in_abrt_post_state(abrt_post_state_t *state, const char *str);
+
+enum {
+ ABRT_POST_WANT_HEADERS = (1 << 0),
+ ABRT_POST_WANT_ERROR_MSG = (1 << 1),
+ ABRT_POST_WANT_BODY = (1 << 2),
+ ABRT_POST_WANT_SSL_VERIFY = (1 << 3),
+};
+enum {
+ /* Must be -1! CURLOPT_POSTFIELDSIZE interprets -1 as "use strlen" */
+ ABRT_POST_DATA_STRING = -1,
+ ABRT_POST_DATA_FROMFILE = -2,
+ ABRT_POST_DATA_FROMFILE_AS_FORM_DATA = -3,
+};
+int
+abrt_post(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char *data,
+ off_t data_size);
+static inline int
+abrt_post_string(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char *str)
+{
+ return abrt_post(state, url, content_type, str, ABRT_POST_DATA_STRING);
+}
+static inline int
+abrt_post_file(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char *filename)
+{
+ return abrt_post(state, url, content_type, filename, ABRT_POST_DATA_FROMFILE);
+}
+static inline int
+abrt_post_file_as_form(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char *filename)
+{
+ return abrt_post(state, url, content_type, filename, ABRT_POST_DATA_FROMFILE_AS_FORM_DATA);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/abrt_dbus.c b/src/lib/abrt_dbus.c
new file mode 100644
index 00000000..6bc155e3
--- /dev/null
+++ b/src/lib/abrt_dbus.c
@@ -0,0 +1,430 @@
+/*
+ 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 <dbus/dbus.h>
+#include <glib.h>
+#include "abrtlib.h"
+#include "abrt_dbus.h"
+
+DBusConnection* g_dbus_conn;
+
+
+/*
+ * Helpers for building DBus messages
+ */
+
+//void store_bool(DBusMessageIter* iter, bool val)
+//{
+// dbus_bool_t db = val;
+// if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &db))
+// die_out_of_memory();
+//}
+void store_int32(DBusMessageIter* iter, int32_t val)
+{
+ if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &val))
+ die_out_of_memory();
+}
+void store_uint32(DBusMessageIter* iter, uint32_t val)
+{
+ if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val))
+ die_out_of_memory();
+}
+void store_int64(DBusMessageIter* iter, int64_t val)
+{
+ if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &val))
+ die_out_of_memory();
+}
+void store_uint64(DBusMessageIter* iter, uint64_t val)
+{
+ if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &val))
+ die_out_of_memory();
+}
+
+/* dbus daemon will simply close our connection if we send broken utf8.
+ * Therefore we must never do that.
+ */
+static char *sanitize_utf8(const char *src)
+{
+ const char *initial_src = src;
+ char *sanitized = NULL;
+ unsigned sanitized_pos = 0;
+
+ while (*src)
+ {
+ int bytes = 0;
+
+ unsigned c = (unsigned char) *src;
+ if (c <= 0x7f)
+ {
+ bytes = 1;
+ goto good_byte;
+ }
+
+ /* Unicode -> utf8: */
+ /* 80-7FF -> 110yyyxx 10xxxxxx */
+ /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
+ /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
+ /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
+ /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
+ do {
+ c <<= 1;
+ bytes++;
+ } while ((c & 0x80) && bytes < 6);
+ if (bytes == 1)
+ {
+ /* A bare "continuation" byte. Say, 80 */
+ goto bad_byte;
+ }
+
+ c = (uint8_t)(c) >> bytes;
+ {
+ const char *pp = src;
+ int cnt = bytes;
+ while (--cnt)
+ {
+ unsigned ch = (unsigned char) *++pp;
+ if ((ch & 0xc0) != 0x80) /* Missing "continuation" byte. Example: e0 80 */
+ {
+ goto bad_byte;
+ }
+ c = (c << 6) + (ch & 0x3f);
+ }
+ }
+ /* TODO */
+ /* Need to check that c isn't produced by overlong encoding */
+ /* Example: 11000000 10000000 converts to NUL */
+ /* 11110000 10000000 10000100 10000000 converts to 0x100 */
+ /* correct encoding: 11000100 10000000 */
+ if (c <= 0x7f) /* crude check: only catches bad encodings which map to chars <= 7f */
+ {
+ goto bad_byte;
+ }
+
+ good_byte:
+ while (--bytes >= 0)
+ {
+ c = (unsigned char) *src++;
+ if (sanitized)
+ {
+ sanitized = (char*) xrealloc(sanitized, sanitized_pos + 2);
+ sanitized[sanitized_pos++] = c;
+ sanitized[sanitized_pos] = '\0';
+ }
+ }
+ continue;
+
+ bad_byte:
+ if (!sanitized)
+ {
+ sanitized_pos = src - initial_src;
+ sanitized = xstrndup(initial_src, sanitized_pos);
+ }
+ sanitized = (char*) xrealloc(sanitized, sanitized_pos + 5);
+ sanitized[sanitized_pos++] = '[';
+ c = (unsigned char) *src++;
+ sanitized[sanitized_pos++] = "0123456789ABCDEF"[c >> 4];
+ sanitized[sanitized_pos++] = "0123456789ABCDEF"[c & 0xf];
+ sanitized[sanitized_pos++] = ']';
+ sanitized[sanitized_pos] = '\0';
+ }
+
+ if (sanitized)
+ VERB2 log("note: bad utf8, converted '%s' -> '%s'", initial_src, sanitized);
+
+ return sanitized; /* usually NULL: the whole string is ok */
+}
+void store_string(DBusMessageIter* iter, const char* val)
+{
+ const char *sanitized = sanitize_utf8(val);
+ if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, sanitized ? &sanitized : &val))
+ die_out_of_memory();
+ free((char*)sanitized);
+}
+
+
+/*
+ * Helpers for parsing DBus messages
+ */
+
+//int load_bool(DBusMessageIter* iter, bool& val)
+//{
+// int type = dbus_message_iter_get_arg_type(iter);
+// if (type != DBUS_TYPE_BOOLEAN)
+// error_msg_and_die("%s expected in dbus message, but not found ('%c')", "bool", type);
+// dbus_bool_t db;
+// dbus_message_iter_get_basic(iter, &db);
+// val = db;
+// return dbus_message_iter_next(iter);
+//}
+int load_int32(DBusMessageIter* iter, int32_t *val)
+{
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_INT32)
+ {
+ error_msg("%s expected in dbus message, but not found ('%c')", "int32", type);
+ return -1;
+ }
+ dbus_message_iter_get_basic(iter, val);
+ return dbus_message_iter_next(iter);
+}
+int load_uint32(DBusMessageIter* iter, uint32_t *val)
+{
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_UINT32)
+ {
+ error_msg("%s expected in dbus message, but not found ('%c')", "uint32", type);
+ return -1;
+ }
+ dbus_message_iter_get_basic(iter, val);
+ return dbus_message_iter_next(iter);
+}
+int load_int64(DBusMessageIter* iter, int64_t *val)
+{
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_INT64)
+ {
+ error_msg("%s expected in dbus message, but not found ('%c')", "int64", type);
+ return -1;
+ }
+ dbus_message_iter_get_basic(iter, val);
+ return dbus_message_iter_next(iter);
+}
+int load_uint64(DBusMessageIter* iter, uint64_t *val)
+{
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_UINT64)
+ {
+ error_msg("%s expected in dbus message, but not found ('%c')", "uint64", type);
+ return -1;
+ }
+ dbus_message_iter_get_basic(iter, val);
+ return dbus_message_iter_next(iter);
+}
+int load_charp(DBusMessageIter* iter, const char** val)
+{
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_STRING)
+ {
+ error_msg("%s expected in dbus message, but not found ('%c')", "string", type);
+ return -1;
+ }
+ dbus_message_iter_get_basic(iter, val);
+//log("load_charp:'%s'", *val);
+ return dbus_message_iter_next(iter);
+}
+
+
+/*
+ * Glib integration machinery
+ */
+
+/* Callback: "glib says dbus fd is active" */
+static gboolean handle_dbus_fd(GIOChannel *gio, GIOCondition condition, gpointer data)
+{
+ DBusWatch *watch = (DBusWatch*)data;
+
+ VERB3 log("%s(gio, condition:%x [bits:IN/PRI/OUT/ERR/HUP...], data)", __func__, (int)condition);
+
+ /* Notify the D-Bus library when a previously-added watch
+ * is ready for reading or writing, or has an exception such as a hangup.
+ */
+ int glib_flags = (int)condition;
+ int dbus_flags = 0;
+ if (glib_flags & G_IO_IN) dbus_flags |= DBUS_WATCH_READABLE;
+ if (glib_flags & G_IO_OUT) dbus_flags |= DBUS_WATCH_WRITABLE;
+ if (glib_flags & G_IO_ERR) dbus_flags |= DBUS_WATCH_ERROR;
+ if (glib_flags & G_IO_HUP) dbus_flags |= DBUS_WATCH_HANGUP;
+ /*
+ * TODO:
+ * If dbus_watch_handle returns FALSE, then the file descriptor
+ * may still be ready for reading or writing, but more memory
+ * is needed in order to do the reading or writing. If you ignore
+ * the FALSE return, your application may spin in a busy loop
+ * on the file descriptor until memory becomes available,
+ * but nothing more catastrophic should happen.
+ */
+ dbus_watch_handle(watch, dbus_flags);
+
+ while (dbus_connection_dispatch(g_dbus_conn) == DBUS_DISPATCH_DATA_REMAINS)
+ VERB3 log("%s: more data to process, looping", __func__);
+ return TRUE; /* "glib, do not remove this event source!" */
+}
+
+typedef struct watch_app_info_t
+{
+ GIOChannel *channel;
+ guint event_source_id;
+ bool watch_enabled;
+} watch_app_info_t;
+/* Callback: "dbus_watch_get_enabled() may return a different value than it did before" */
+static void toggled_watch(DBusWatch *watch, void* data)
+{
+ VERB3 log("%s(watch:%p, data)", __func__, watch);
+
+ watch_app_info_t* app_info = (watch_app_info_t*)dbus_watch_get_data(watch);
+ if (dbus_watch_get_enabled(watch))
+ {
+ if (!app_info->watch_enabled)
+ {
+ app_info->watch_enabled = true;
+ int dbus_flags = dbus_watch_get_flags(watch);
+ int glib_flags = 0;
+ if (dbus_flags & DBUS_WATCH_READABLE) glib_flags |= G_IO_IN;
+ if (dbus_flags & DBUS_WATCH_WRITABLE) glib_flags |= G_IO_OUT;
+ VERB3 log(" adding watch to glib main loop. dbus_flags:%x glib_flags:%x", dbus_flags, glib_flags);
+ app_info->event_source_id = g_io_add_watch(app_info->channel, (GIOCondition)glib_flags, handle_dbus_fd, watch);
+ }
+ /* else: it was already enabled */
+ } else {
+ if (app_info->watch_enabled)
+ {
+ app_info->watch_enabled = false;
+ /* does it free the hidden GSource too? */
+ VERB3 log(" removing watch from glib main loop");
+ g_source_remove(app_info->event_source_id);
+ }
+ /* else: it was already disabled */
+ }
+}
+/* Callback: "libdbus needs a new watch to be monitored by the main loop" */
+static dbus_bool_t add_watch(DBusWatch *watch, void* data)
+{
+ VERB3 log("%s(watch:%p, data)", __func__, watch);
+
+ watch_app_info_t* app_info = (watch_app_info_t*)xzalloc(sizeof(*app_info));
+ dbus_watch_set_data(watch, app_info, free);
+
+ int fd = dbus_watch_get_unix_fd(watch);
+ VERB3 log(" dbus_watch_get_unix_fd():%d", fd);
+ app_info->channel = g_io_channel_unix_new(fd);
+ /* _unconditionally_ adding it to event loop would be an error */
+ toggled_watch(watch, data);
+ return TRUE;
+}
+/* Callback: "libdbus no longer needs a watch to be monitored by the main loop" */
+static void remove_watch(DBusWatch *watch, void* data)
+{
+ VERB3 log("%s()", __func__);
+ watch_app_info_t* app_info = (watch_app_info_t*)dbus_watch_get_data(watch);
+ if (app_info->watch_enabled)
+ {
+ app_info->watch_enabled = false;
+ g_source_remove(app_info->event_source_id);
+ }
+ g_io_channel_unref(app_info->channel);
+}
+
+/* Callback: "libdbus needs a new timeout to be monitored by the main loop" */
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void* data)
+{
+ VERB3 log("%s()", __func__);
+ return TRUE;
+}
+/* Callback: "libdbus no longer needs a timeout to be monitored by the main loop" */
+static void remove_timeout(DBusTimeout *timeout, void* data)
+{
+ VERB3 log("%s()", __func__);
+}
+/* Callback: "dbus_timeout_get_enabled() may return a different value than it did before" */
+static void timeout_toggled(DBusTimeout *timeout, void* data)
+{
+//seems to be never called, let's make it noisy
+ error_msg_and_die("%s(): FIXME: some dbus machinery is missing here", __func__);
+}
+
+/* Callback: "DBusObjectPathVTable is unregistered (or its connection is freed)" */
+static void unregister_vtable(DBusConnection *conn, void* data)
+{
+ VERB3 log("%s()", __func__);
+}
+
+/*
+ * Initialization works as follows:
+ *
+ * we have a DBusConnection* (say, obtained with dbus_bus_get)
+ * we call dbus_connection_set_watch_functions
+ * libdbus calls back add_watch(watch:0x2341090, data), this watch is for writing
+ * we call toggled_watch, but it finds that watch is not to be enabled yet
+ * libdbus calls back add_watch(watch:0x23410e0, data), this watch is for reading
+ * we call toggled_watch, it adds watch's fd to glib main loop with POLLIN
+ * (note: these watches are different objects, but they have the same fd)
+ * we call dbus_connection_set_timeout_functions
+ * we call dbus_connection_register_object_path
+ *
+ * Note: if user will later call dbus_bus_request_name(conn, ...):
+ * libdbus calls back add_timeout()
+ * libdbus calls back remove_timeout()
+ * note - no callback to timeout_toggled()!
+ * (therefore there is no code yet in timeout_toggled (see above), it's not used)
+ */
+void attach_dbus_conn_to_glib_main_loop(DBusConnection* conn,
+ const char* object_path,
+ DBusHandlerResult (*message_received_func)(DBusConnection *conn, DBusMessage *msg, void* data)
+) {
+ if (g_dbus_conn)
+ error_msg_and_die("Internal bug: can't connect to more than one dbus");
+ g_dbus_conn = conn;
+
+//do we need this? why?
+//log("dbus_connection_set_dispatch_status_function");
+// dbus_connection_set_dispatch_status_function(conn,
+// dispatch, /* void dispatch(DBusConnection *conn, DBusDispatchStatus new_status, void* data) */
+// NULL, /* data */
+// NULL /* free_data_function */
+// )
+ VERB3 log("dbus_connection_set_watch_functions");
+ if (!dbus_connection_set_watch_functions(conn,
+ add_watch,
+ remove_watch,
+ toggled_watch,
+ NULL, /* data */
+ NULL /* free_data_function */
+ )
+ ) {
+ die_out_of_memory();
+ }
+ VERB3 log("dbus_connection_set_timeout_functions");
+ if (!dbus_connection_set_timeout_functions(conn,
+ add_timeout,
+ remove_timeout,
+ timeout_toggled,
+ NULL, /* data */
+ NULL /* free_data_function */
+ )
+ ) {
+ die_out_of_memory();
+ }
+
+ if (object_path && message_received_func)
+ {
+ /* Table */
+ const DBusObjectPathVTable vtable = {
+ /* .unregister_function = */ unregister_vtable,
+ /* .message_function = */ message_received_func,
+ };
+ VERB3 log("dbus_connection_register_object_path");
+ if (!dbus_connection_register_object_path(conn,
+ object_path,
+ &vtable,
+ NULL /* data */
+ )
+ ) {
+ die_out_of_memory();
+ }
+ }
+}
diff --git a/src/lib/abrt_dbus.h b/src/lib/abrt_dbus.h
new file mode 100644
index 00000000..cdc963ca
--- /dev/null
+++ b/src/lib/abrt_dbus.h
@@ -0,0 +1,324 @@
+/*
+ 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 ABRT_DBUS_H
+#define ABRT_DBUS_H
+
+#include <dbus/dbus.h>
+#include "abrtlib.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern DBusConnection* g_dbus_conn;
+
+/*
+ * Glib integration machinery
+ */
+
+/* Hook up to DBus and to glib main loop.
+ * Usage cases:
+ *
+ * - server:
+ * conn = dbus_bus_get(DBUS_BUS_SYSTEM/SESSION, &err);
+ * attach_dbus_conn_to_glib_main_loop(conn, "/some/path", handler_of_calls_to_some_path);
+ * rc = dbus_bus_request_name(conn, "server.name", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
+ *
+ * - client which does not receive signals (only makes calls and emits signals):
+ * conn = dbus_bus_get(DBUS_BUS_SYSTEM/SESSION, &err);
+ * // needed only if you need to use async dbus calls (not shown below):
+ * attach_dbus_conn_to_glib_main_loop(conn, NULL, NULL);
+ * // syncronous method call:
+ * msg = dbus_message_new_method_call("some.serv", "/path/on/serv", "optional.iface.on.serv", "method_name");
+ * reply = dbus_connection_send_with_reply_and_block(conn, msg, timeout, &err);
+ * // emitting signal:
+ * msg = dbus_message_new_signal("/path/sig/emitted/from", "iface.sig.emitted.from", "sig_name");
+ * // (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
+ *
+ * - client which receives and processes signals:
+ * conn = dbus_bus_get(DBUS_BUS_SYSTEM/SESSION, &err);
+ * attach_dbus_conn_to_glib_main_loop(conn, NULL, NULL);
+ * dbus_connection_add_filter(conn, handle_message, NULL, NULL)
+ * dbus_bus_add_match(system_conn, "type='signal',...", &err);
+ * // signal is a dbus message which looks like this:
+ * // sender=XXX dest=YYY(or null) path=/path/sig/emitted/from interface=iface.sig.emitted.from member=sig_name
+ * // and handler_for_signals(conn,msg,opaque) will be called by glib
+ * // main loop to process received signals (and other messages
+ * // if you ask for them in dbus_bus_add_match[es], but this
+ * // would turn you into a server if you handle them too) ;]
+ */
+void attach_dbus_conn_to_glib_main_loop(DBusConnection* conn,
+ /* NULL if you are just a client */
+ const char* object_path_to_register,
+ /* makes sense only if you use object_path_to_register: */
+ DBusHandlerResult (*message_received_func)(DBusConnection *conn, DBusMessage *msg, void* data)
+);
+
+/*
+ * Helpers for building DBus messages
+ */
+//void store_bool(DBusMessageIter* iter, bool val);
+void store_int32(DBusMessageIter* iter, int32_t val);
+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);
+
+/*
+ * Helpers for parsing DBus messages
+ */
+enum {
+ ABRT_DBUS_ERROR = -1,
+ ABRT_DBUS_LAST_FIELD = 0,
+ ABRT_DBUS_MORE_FIELDS = 1,
+ /* note that dbus_message_iter_next() returns FALSE on last field
+ * and TRUE if there are more fields.
+ * It maps exactly on the above constants. */
+};
+/* Checks type, loads data, advances to the next arg.
+ * Returns TRUE if next arg exists.
+ */
+//int load_bool(DBusMessageIter* iter, bool& val);
+int load_int32(DBusMessageIter* iter, int32_t *val);
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/* C++ style stuff */
+#ifdef __cplusplus
+
+#include <map>
+#include <vector>
+
+/*
+ * Helpers for building DBus messages
+ */
+
+//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); }
+static inline void store_val(DBusMessageIter* iter, int64_t val) { store_int64(iter, val); }
+static inline void store_val(DBusMessageIter* iter, uint64_t val) { store_uint64(iter, val); }
+static inline void store_val(DBusMessageIter* iter, const char* val) { store_string(iter, val); }
+static inline void store_val(DBusMessageIter* iter, const std::string& val) { store_string(iter, val.c_str()); }
+
+/* Templates for vector and map */
+template <typename T> struct abrt_dbus_type {};
+//template <> struct abrt_dbus_type<bool> { static const char* csig() { return "b"; } };
+template <> struct abrt_dbus_type<int32_t> { static const char* csig() { return "i"; } static std::string sig(); };
+template <> struct abrt_dbus_type<uint32_t> { static const char* csig() { return "u"; } static std::string sig(); };
+template <> struct abrt_dbus_type<int64_t> { static const char* csig() { return "x"; } static std::string sig(); };
+template <> struct abrt_dbus_type<uint64_t> { static const char* csig() { return "t"; } static std::string sig(); };
+template <> struct abrt_dbus_type<std::string> { static const char* csig() { return "s"; } static std::string sig(); };
+#define ABRT_DBUS_SIG(T) (abrt_dbus_type<T>::csig() ? abrt_dbus_type<T>::csig() : abrt_dbus_type<T>::sig().c_str())
+template <typename E>
+struct abrt_dbus_type< std::vector<E> > {
+ static const char* csig() { return NULL; }
+ static std::string sig() { return ssprintf("a%s", ABRT_DBUS_SIG(E)); }
+};
+template <typename K, typename V>
+struct abrt_dbus_type< std::map<K,V> > {
+ static const char* csig() { return NULL; }
+ static std::string sig() { return ssprintf("a{%s%s}", ABRT_DBUS_SIG(K), ABRT_DBUS_SIG(V)); }
+};
+
+template<typename E>
+static void store_vector(DBusMessageIter* iter, const std::vector<E>& val)
+{
+ DBusMessageIter sub_iter;
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, ABRT_DBUS_SIG(E), &sub_iter))
+ die_out_of_memory();
+
+ typename std::vector<E>::const_iterator vit = val.begin();
+ for (; vit != val.end(); ++vit)
+ {
+ store_val(&sub_iter, *vit);
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub_iter))
+ die_out_of_memory();
+}
+/*
+template<>
+static void store_vector(DBus::MessageIter &iter, const 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>
+static void store_map(DBusMessageIter* iter, const std::map<K,V>& val)
+{
+ DBusMessageIter sub_iter;
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ ssprintf("{%s%s}", ABRT_DBUS_SIG(K), ABRT_DBUS_SIG(V)).c_str(),
+ &sub_iter))
+ die_out_of_memory();
+
+ typename std::map<K,V>::const_iterator mit = val.begin();
+ for (; mit != val.end(); ++mit)
+ {
+ 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_val(&sub_sub_iter, mit->first);
+ store_val(&sub_sub_iter, mit->second);
+ if (!dbus_message_iter_close_container(&sub_iter, &sub_sub_iter))
+ die_out_of_memory();
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub_iter))
+ die_out_of_memory();
+}
+
+template<typename E>
+static inline void store_val(DBusMessageIter* iter, const std::vector<E>& val) { store_vector(iter, val); }
+template<typename K, typename V>
+static inline void store_val(DBusMessageIter* iter, const std::map<K,V>& val) { store_map(iter, val); }
+
+
+/*
+ * Helpers for parsing DBus messages
+ */
+
+//static inline int load_val(DBusMessageIter* iter, bool &val) { return load_bool(iter, &val); }
+static inline int load_val(DBusMessageIter* iter, int32_t &val) { return load_int32(iter, &val); }
+static inline int load_val(DBusMessageIter* iter, uint32_t &val) { return load_uint32(iter, &val); }
+static inline int load_val(DBusMessageIter* iter, int64_t &val) { return load_int64(iter, &val); }
+static inline int load_val(DBusMessageIter* iter, uint64_t &val) { return load_uint64(iter, &val); }
+static inline int load_val(DBusMessageIter* iter, const char*& val) { return load_charp(iter, &val); }
+static inline int load_val(DBusMessageIter* iter, std::string& val)
+{
+ const char* str;
+ int r = load_charp(iter, &str);
+ val = str;
+ return r;
+}
+
+/* Templates for vector and map */
+template<typename E>
+static int load_vector(DBusMessageIter* iter, std::vector<E>& val)
+{
+ 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);
+
+ 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 {
+ E elem;
+//cnt++;
+ r = load_val(&sub_iter, elem);
+ if (r < 0)
+ return r;
+ val.push_back(elem);
+ } while (r == ABRT_DBUS_MORE_FIELDS);
+ }
+//log("%s: %d elems", __func__, cnt);
+
+ return dbus_message_iter_next(iter);
+}
+/*
+template<>
+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>
+static int load_map(DBusMessageIter* iter, std::map<K,V>& val)
+{
+ 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);
+
+ 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);
+ return -1;
+ }
+
+ DBusMessageIter sub_sub_iter;
+ dbus_message_iter_recurse(&sub_iter, &sub_sub_iter);
+
+ K key;
+ r = load_val(&sub_sub_iter, key);
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ if (r == ABRT_DBUS_LAST_FIELD)
+ error_msg("malformed map element in dbus message");
+ return -1;
+ }
+ V value;
+ r = load_val(&sub_sub_iter, value);
+ if (r != ABRT_DBUS_LAST_FIELD)
+ {
+ if (r == ABRT_DBUS_MORE_FIELDS)
+ error_msg("malformed map element in dbus message");
+ return -1;
+ }
+ val[key] = value;
+//cnt++;
+ next_exists = dbus_message_iter_next(&sub_iter);
+ } while (next_exists);
+//log("%s: %d elems", __func__, cnt);
+
+ return dbus_message_iter_next(iter);
+}
+
+template<typename E>
+static inline int load_val(DBusMessageIter* iter, std::vector<E>& val) { return load_vector(iter, val); }
+template<typename K, typename V>
+static inline int load_val(DBusMessageIter* iter, std::map<K,V>& val) { return load_map(iter, val); }
+
+#endif /* __cplusplus */
+
+#endif
diff --git a/src/lib/abrt_packages.c b/src/lib/abrt_packages.c
new file mode 100644
index 00000000..bbf0e729
--- /dev/null
+++ b/src/lib/abrt_packages.c
@@ -0,0 +1,43 @@
+/*
+ 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"
+
+/* cuts the name from the NVR format: foo-1.2.3-1.el6
+ returns a newly allocated string
+*/
+char* get_package_name_from_NVR_or_NULL(const char* packageNVR)
+{
+ char* package_name = NULL;
+ if (packageNVR != NULL)
+ {
+ VERB1 log("packageNVR %s", packageNVR);
+ package_name = xstrdup(packageNVR);
+ char *pos = strrchr(package_name, '-');
+ if (pos != NULL)
+ {
+ *pos = 0;
+ pos = strrchr(package_name, '-');
+ if (pos != NULL)
+ {
+ *pos = 0;
+ }
+ }
+ }
+ return package_name;
+}
diff --git a/src/lib/abrt_packages.h b/src/lib/abrt_packages.h
new file mode 100644
index 00000000..6a952f5e
--- /dev/null
+++ b/src/lib/abrt_packages.h
@@ -0,0 +1,33 @@
+/*
+ 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 ABRT_PACKAGES_H
+#define ABRT_PACKAGES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char* get_package_name_from_NVR_or_NULL(const char* packageNVR);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/abrt_xmlrpc.cpp b/src/lib/abrt_xmlrpc.cpp
new file mode 100644
index 00000000..bf74f05b
--- /dev/null
+++ b/src/lib/abrt_xmlrpc.cpp
@@ -0,0 +1,97 @@
+/*
+ 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"
+#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());
+}
+
+void throw_if_xml_fault_occurred(xmlrpc_env *env)
+{
+ if (env->fault_occurred)
+ {
+ throw_xml_fault(env);
+ }
+}
+
+void abrt_xmlrpc_conn::new_xmlrpc_client(const char* url, bool ssl_verify)
+{
+ m_pClient = NULL;
+ m_pServer_info = NULL;
+
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ /* This should be done at program startup, once.
+ * We do it in abrtd's main */
+ /* xmlrpc_client_setup_global_const(&env); */
+
+ struct xmlrpc_curl_xportparms curlParms;
+ memset(&curlParms, 0, sizeof(curlParms));
+ /* curlParms.network_interface = NULL; - done by memset */
+ curlParms.no_ssl_verifypeer = !ssl_verify;
+ curlParms.no_ssl_verifyhost = !ssl_verify;
+#ifdef VERSION
+ curlParms.user_agent = PACKAGE_NAME"/"VERSION;
+#else
+ curlParms.user_agent = "abrt";
+#endif
+
+ struct xmlrpc_clientparms clientParms;
+ memset(&clientParms, 0, sizeof(clientParms));
+ clientParms.transport = "curl";
+ clientParms.transportparmsP = &curlParms;
+ clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent);
+
+ xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS,
+ PACKAGE_NAME, VERSION,
+ &clientParms, XMLRPC_CPSIZE(transportparm_size),
+ &m_pClient);
+ if (env.fault_occurred)
+ throw_xml_fault(&env);
+
+ m_pServer_info = xmlrpc_server_info_new(&env, url);
+ if (env.fault_occurred)
+ {
+ xmlrpc_client_destroy(m_pClient);
+ m_pClient = NULL;
+ throw_xml_fault(&env);
+ }
+}
+
+void abrt_xmlrpc_conn::destroy_xmlrpc_client()
+{
+ if (m_pServer_info)
+ {
+ xmlrpc_server_info_free(m_pServer_info);
+ m_pServer_info = NULL;
+ }
+ if (m_pClient)
+ {
+ xmlrpc_client_destroy(m_pClient);
+ m_pClient = NULL;
+ }
+}
diff --git a/src/lib/abrt_xmlrpc.h b/src/lib/abrt_xmlrpc.h
new file mode 100644
index 00000000..ad1a87d3
--- /dev/null
+++ b/src/lib/abrt_xmlrpc.h
@@ -0,0 +1,48 @@
+/*
+ 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 ABRT_XMLRPC_H_
+#define ABRT_XMLRPC_H_ 1
+
+#include <curl/curl.h>
+#include <xmlrpc-c/base.h>
+#include <xmlrpc-c/client.h>
+
+/*
+ * 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;
+
+ abrt_xmlrpc_conn(const char* url, bool ssl_verify) { new_xmlrpc_client(url, ssl_verify); }
+ /* this never throws exceptions - calls C functions only */
+ ~abrt_xmlrpc_conn() { destroy_xmlrpc_client(); }
+
+ void new_xmlrpc_client(const char* url, bool ssl_verify);
+ void destroy_xmlrpc_client();
+};
+
+/* Utility functions */
+void throw_xml_fault(xmlrpc_env *env);
+void throw_if_xml_fault_occurred(xmlrpc_env *env);
+
+#endif
diff --git a/src/lib/append_to_malloced_string.c b/src/lib/append_to_malloced_string.c
new file mode 100644
index 00000000..430cef0b
--- /dev/null
+++ b/src/lib/append_to_malloced_string.c
@@ -0,0 +1,27 @@
+/*
+ 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"
+
+char *append_to_malloced_string(char *mstr, const char *append)
+{
+ unsigned mlen = strlen(mstr);
+ mstr = (char*) xrealloc(mstr, mlen + strlen(append) + 1);
+ strcpy(mstr + mlen, append);
+ return mstr;
+}
diff --git a/src/lib/concat_path_file.c b/src/lib/concat_path_file.c
new file mode 100644
index 00000000..4c946628
--- /dev/null
+++ b/src/lib/concat_path_file.c
@@ -0,0 +1,25 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2001 Erik Andersen
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+/* Concatenate path and filename to new allocated buffer.
+ * Add '/' only as needed (no duplicate // are produced).
+ * If path is NULL, it is assumed to be "/".
+ * filename should not be NULL.
+ */
+
+#include "abrtlib.h"
+
+char *concat_path_file(const char *path, const char *filename)
+{
+ if (!path)
+ path = "";
+ const char *end = path + strlen(path);
+ while (*filename == '/')
+ filename++;
+ return xasprintf("%s%s%s", path, (end != path && end[-1] != '/' ? "/" : ""), filename);
+}
diff --git a/src/lib/copyfd.c b/src/lib/copyfd.c
new file mode 100644
index 00000000..bfe0fa49
--- /dev/null
+++ b/src/lib/copyfd.c
@@ -0,0 +1,172 @@
+/*
+ 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.
+*/
+
+/*
+ * Utility routines.
+ *
+ */
+#include "abrtlib.h"
+
+#define CONFIG_FEATURE_COPYBUF_KB 4
+
+static const char msg_write_error[] = "write error";
+static const char msg_read_error[] = "read error";
+
+static off_t full_fd_action(int src_fd, int dst_fd, off_t size, int flags)
+{
+ int status = -1;
+ off_t total = 0;
+ int last_was_seek = 0;
+#if CONFIG_FEATURE_COPYBUF_KB <= 4
+ char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
+ enum { buffer_size = sizeof(buffer) };
+#else
+ char *buffer;
+ int buffer_size;
+
+ /* We want page-aligned buffer, just in case kernel is clever
+ * and can do page-aligned io more efficiently */
+ buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ /* ignored: */ -1, 0);
+ buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
+ if (buffer == MAP_FAILED) {
+ buffer = alloca(4 * 1024);
+ buffer_size = 4 * 1024;
+ }
+#endif
+
+ if (src_fd < 0)
+ goto out;
+
+ if (!size) {
+ size = buffer_size;
+ status = 1; /* copy until eof */
+ }
+
+ while (1) {
+ ssize_t rd;
+
+ rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size);
+
+ if (!rd) { /* eof - all done */
+ if (last_was_seek) {
+ if (lseek(dst_fd, -1, SEEK_CUR) < 0
+ || safe_write(dst_fd, "", 1) != 1
+ ) {
+ perror_msg("%s", msg_write_error);
+ break;
+ }
+ }
+ status = 0;
+ break;
+ }
+ if (rd < 0) {
+ perror_msg("%s", msg_read_error);
+ break;
+ }
+ /* dst_fd == -1 is a fake, else... */
+ if (dst_fd >= 0) {
+ if (flags & COPYFD_SPARSE) {
+ ssize_t cnt = rd;
+ while (--cnt >= 0)
+ if (buffer[cnt] != 0)
+ goto need2write;
+ if (lseek(dst_fd, rd, SEEK_CUR) < 0) {
+ flags &= ~COPYFD_SPARSE;
+ goto need2write;
+ }
+ last_was_seek = 1;
+ } else {
+ need2write:
+ {
+ ssize_t wr = full_write(dst_fd, buffer, rd);
+ if (wr < rd) {
+ perror_msg("%s", msg_write_error);
+ break;
+ }
+ last_was_seek = 0;
+ }
+ }
+ }
+ total += rd;
+ if (status < 0) { /* if we aren't copying till EOF... */
+ size -= rd;
+ if (!size) {
+ /* 'size' bytes copied - all done */
+ status = 0;
+ break;
+ }
+ }
+ }
+ out:
+
+#if CONFIG_FEATURE_COPYBUF_KB > 4
+ if (buffer_size != 4 * 1024)
+ munmap(buffer, buffer_size);
+#endif
+ return status ? -1 : total;
+}
+
+off_t copyfd_size(int fd1, int fd2, off_t size, int flags)
+{
+ if (size) {
+ return full_fd_action(fd1, fd2, size, flags);
+ }
+ return 0;
+}
+
+void copyfd_exact_size(int fd1, int fd2, off_t size)
+{
+ off_t sz = copyfd_size(fd1, fd2, size, /*flags:*/ 0);
+ if (sz == size)
+ return;
+ if (sz != -1)
+ error_msg_and_die("short read");
+ /* if sz == -1, copyfd_XX already complained */
+ xfunc_die();
+}
+
+off_t copyfd_eof(int fd1, int fd2, int flags)
+{
+ return full_fd_action(fd1, fd2, 0, flags);
+}
+
+off_t copy_file(const char *src_name, const char *dst_name, int mode)
+{
+ off_t r;
+ int src = open(src_name, O_RDONLY);
+ if (src < 0)
+ {
+ perror_msg("Can't open '%s'", src_name);
+ return -1;
+ }
+ int dst = open(dst_name, O_WRONLY | O_TRUNC | O_CREAT, mode);
+ if (dst < 0)
+ {
+ close(src);
+ perror_msg("Can't open '%s'", dst_name);
+ return -1;
+ }
+ r = copyfd_eof(src, dst, /*flags:*/ 0);
+ close(src);
+ close(dst);
+ return r;
+}
diff --git a/src/lib/daemon.c b/src/lib/daemon.c
new file mode 100644
index 00000000..944aef0c
--- /dev/null
+++ b/src/lib/daemon.c
@@ -0,0 +1,139 @@
+/*
+ Copyright (C) 2009 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"
+
+#define VAR_RUN_PID_FILE VAR_RUN"/abrtd.pid"
+
+static char *append_escaped(char *start, const char *s)
+{
+ char hex_char_buf[] = "\\x00";
+
+ *start++ = ' ';
+ char *dst = start;
+ const unsigned char *p = (unsigned char *)s;
+
+ while (1)
+ {
+ const unsigned char *old_p = p;
+ while (*p > ' ' && *p <= 0x7e && *p != '\"' && *p != '\'' && *p != '\\')
+ p++;
+ if (dst == start)
+ {
+ if (p != (unsigned char *)s && *p == '\0')
+ {
+ /* entire word does not need escaping and quoting */
+ strcpy(dst, s);
+ dst += strlen(s);
+ return dst;
+ }
+ *dst++ = '\'';
+ }
+
+ strncpy(dst, (char *)old_p, (p - old_p));
+ dst += (p - old_p);
+
+ if (*p == '\0')
+ {
+ *dst++ = '\'';
+ *dst = '\0';
+ return dst;
+ }
+ const char *a;
+ switch (*p)
+ {
+ case '\r': a = "\\r"; break;
+ case '\n': a = "\\n"; break;
+ case '\t': a = "\\t"; break;
+ case '\'': a = "\\\'"; break;
+ case '\"': a = "\\\""; break;
+ case '\\': a = "\\\\"; break;
+ case ' ': a = " "; break;
+ default:
+ hex_char_buf[2] = "0123456789abcdef"[*p >> 4];
+ hex_char_buf[3] = "0123456789abcdef"[*p & 0xf];
+ a = hex_char_buf;
+ }
+ strcpy(dst, a);
+ dst += strlen(a);
+ p++;
+ }
+}
+
+// taken from kernel
+#define COMMAND_LINE_SIZE 2048
+char* get_cmdline(pid_t pid)
+{
+ char path[sizeof("/proc/%lu/cmdline") + sizeof(long)*3];
+ char cmdline[COMMAND_LINE_SIZE];
+ char escaped_cmdline[COMMAND_LINE_SIZE*4 + 4];
+
+ escaped_cmdline[1] = '\0';
+ sprintf(path, "/proc/%lu/cmdline", (long)pid);
+ int fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ {
+ int len = read(fd, cmdline, sizeof(cmdline) - 1);
+ close(fd);
+
+ if (len > 0)
+ {
+ cmdline[len] = '\0';
+ char *src = cmdline;
+ char *dst = escaped_cmdline;
+ while ((src - cmdline) < len)
+ {
+ dst = append_escaped(dst, src);
+ src += strlen(src) + 1;
+ }
+ }
+ }
+
+ return xstrdup(escaped_cmdline + 1); /* +1 skips extraneous leading space */
+}
+
+int daemon_is_ok()
+{
+ int fd = open(VAR_RUN_PID_FILE, O_RDONLY);
+ if (fd < 0)
+ {
+ return 0;
+ }
+
+ char pid[sizeof(pid_t)*3 + 2];
+ int len = read(fd, pid, sizeof(pid)-1);
+ close(fd);
+ if (len <= 0)
+ return 0;
+
+ pid[len] = '\0';
+ *strchrnul(pid, '\n') = '\0';
+ /* paranoia: we don't want to check /proc//stat or /proc///stat */
+ if (pid[0] == '\0' || pid[0] == '/')
+ return 0;
+
+ /* TODO: maybe readlink and check that it is "xxx/abrt"? */
+ char path[sizeof("/proc/%s/stat") + sizeof(pid)];
+ sprintf(path, "/proc/%s/stat", pid);
+ struct stat sb;
+ if (stat(path, &sb) == -1)
+ {
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/lib/database.c b/src/lib/database.c
new file mode 100644
index 00000000..2b742ebf
--- /dev/null
+++ b/src/lib/database.c
@@ -0,0 +1,73 @@
+/*
+ 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"
+#include "database.h"
+
+struct db_row *db_rowcpy_from_list(GList* list)
+{
+ GList *first = g_list_first(list);
+ struct db_row *row = (struct db_row*)xzalloc(sizeof(struct db_row));
+ struct db_row *src_row = (struct db_row*)first->data;
+ /* All fields are initialized below, copying is not needed
+ * memcpy(row, (struct db_row*)first->data, sizeof(struct db_row));
+ */
+
+ row->db_uuid = xstrdup(src_row->db_uuid);
+ row->db_uid = xstrdup(src_row->db_uid);
+ row->db_inform_all = xstrdup(src_row->db_inform_all);
+ row->db_dump_dir = xstrdup(src_row->db_dump_dir);
+ row->db_count = xstrdup(src_row->db_count);
+ row->db_reported = xstrdup(src_row->db_reported);
+ row->db_message = xstrdup(src_row->db_message);
+ row->db_time = xstrdup(src_row->db_time);
+
+ return row;
+}
+
+void db_row_free(struct db_row *row)
+{
+ if (!row)
+ return;
+
+ free(row->db_uuid);
+ free(row->db_uid);
+ free(row->db_inform_all);
+ free(row->db_dump_dir);
+ free(row->db_count);
+ free(row->db_reported);
+ free(row->db_message);
+ free(row->db_time);
+
+ free(row);
+}
+
+void db_list_free(GList *list)
+{
+ if (!list)
+ return;
+
+ for (GList *li = list; li != NULL; li = g_list_next(li))
+ {
+ struct db_row *row = (struct db_row*)li->data;
+ db_row_free(row);
+ }
+ g_list_free(list);
+}
+
diff --git a/src/lib/dirsize.c b/src/lib/dirsize.c
new file mode 100644
index 00000000..be927ad5
--- /dev/null
+++ b/src/lib/dirsize.c
@@ -0,0 +1,110 @@
+/*
+ Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
+ Copyright (C) 2009 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"
+
+double get_dirsize(const char *pPath)
+{
+ DIR *dp = opendir(pPath);
+ if (dp == NULL)
+ return 0;
+
+ struct dirent *ep;
+ struct stat statbuf;
+ double size = 0;
+ while ((ep = readdir(dp)) != NULL)
+ {
+ if (dot_or_dotdot(ep->d_name))
+ continue;
+ char *dname = concat_path_file(pPath, ep->d_name);
+ if (lstat(dname, &statbuf) != 0)
+ {
+ free(dname);
+ continue;
+ }
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ size += get_dirsize(dname);
+ }
+ else if (S_ISREG(statbuf.st_mode))
+ {
+ size += statbuf.st_size;
+ }
+ free(dname);
+ }
+ closedir(dp);
+ return size;
+}
+
+double get_dirsize_find_largest_dir(
+ const char *pPath,
+ char **worst_dir,
+ const char *excluded)
+{
+ if (worst_dir)
+ *worst_dir = NULL;
+
+ DIR *dp = opendir(pPath);
+ if (dp == NULL)
+ return 0;
+
+ struct dirent *ep;
+ struct stat statbuf;
+ double size = 0;
+ double maxsz = 0;
+ while ((ep = readdir(dp)) != NULL)
+ {
+ if (dot_or_dotdot(ep->d_name))
+ continue;
+ char *dname = concat_path_file(pPath, ep->d_name);
+ if (lstat(dname, &statbuf) != 0)
+ {
+ free(dname);
+ continue;
+ }
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ double sz = get_dirsize(dname);
+ size += sz;
+
+ if (worst_dir && (!excluded || strcmp(excluded, ep->d_name) != 0))
+ {
+ /* Calculate "weighted" size and age
+ * w = sz_kbytes * age_mins */
+ sz /= 1024;
+ long age = (time(NULL) - statbuf.st_mtime) / 60;
+ if (age > 0)
+ sz *= age;
+
+ if (sz > maxsz)
+ {
+ maxsz = sz;
+ free(*worst_dir);
+ *worst_dir = xstrdup(ep->d_name);
+ }
+ }
+ }
+ else if (S_ISREG(statbuf.st_mode))
+ {
+ size += statbuf.st_size;
+ }
+ free(dname);
+ }
+ closedir(dp);
+ return size;
+}
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
new file mode 100644
index 00000000..0328bb26
--- /dev/null
+++ b/src/lib/dump_dir.c
@@ -0,0 +1,504 @@
+/*
+ DebugDump.cpp
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 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 <sys/utsname.h>
+#include "abrtlib.h"
+#include "strbuf.h"
+
+// TODO:
+//
+// 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.
+
+static char *load_text_file(const char *path, unsigned flags);
+
+static bool isdigit_str(const char *str)
+{
+ do
+ {
+ if (*str < '0' || *str > '9') return false;
+ str++;
+ } while (*str);
+ return true;
+}
+
+static bool exist_file_dir(const char *path)
+{
+ struct stat buf;
+ if (stat(path, &buf) == 0)
+ {
+ if (S_ISDIR(buf.st_mode) || S_ISREG(buf.st_mode))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool 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);
+
+ char pid_buf[sizeof(pid_t)*3 + 4];
+ ssize_t r = readlink(lock_file, pid_buf, sizeof(pid_buf) - 1);
+ if (r < 0)
+ {
+ if (errno == ENOENT)
+ {
+ /* Looks like lock_file was deleted */
+ usleep(10 * 1000); /* avoid CPU eating loop */
+ continue;
+ }
+ perror_msg_and_die("Can't read lock file '%s'", lock_file);
+ }
+ pid_buf[r] = '\0';
+
+ if (strcmp(pid_buf, pid) == 0)
+ {
+ log("Lock file '%s' is already locked by us", lock_file);
+ return false;
+ }
+ if (isdigit_str(pid_buf))
+ {
+ char pid_str[sizeof("/proc/") + strlen(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;
+ }
+ 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);
+ }
+ }
+
+ VERB1 log("Locked '%s'", lock_file);
+ return true;
+}
+
+static void dd_lock(struct dump_dir *dd)
+{
+ 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);
+
+ 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)
+ {
+ sleep(1); /* was 0.5 seconds */
+ }
+}
+
+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);
+ xunlink(lock_buf);
+ VERB1 log("Unlocked '%s'", lock_buf);
+ }
+}
+
+static inline struct dump_dir *dd_init(void)
+{
+ return (struct dump_dir*)xzalloc(sizeof(struct dump_dir));
+}
+
+int dd_exist(struct dump_dir *dd, const char *path)
+{
+ char *full_path = concat_path_file(dd->dd_dir, path);
+ int ret = exist_file_dir(full_path);
+ free(full_path);
+ return ret;
+}
+
+void dd_close(struct dump_dir *dd)
+{
+ if (!dd)
+ return;
+
+ dd_unlock(dd);
+ if (dd->next_dir)
+ {
+ closedir(dd->next_dir);
+ /* free(dd->next_dir); - WRONG! */
+ }
+
+ free(dd->dd_dir);
+ free(dd);
+}
+
+static char* rm_trailing_slashes(const char *dir)
+{
+ unsigned len = strlen(dir);
+ while (len != 0 && dir[len-1] == '/')
+ len--;
+ return xstrndup(dir, len);
+}
+
+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);
+
+ 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;
+ }
+
+ /* 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)
+ {
+ /*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;
+ }
+
+ return dd;
+}
+
+/* Create a fresh empty debug dump dir.
+ *
+ * Security: we should not allow users to write new files or write
+ * into existing ones, but they should be able to read them.
+ *
+ * @param uid
+ * Crashed application's User Id
+ *
+ * We currently have only three callers:
+ * kernel oops hook: uid=0
+ * this hook runs under 0:0
+ * ccpp hook: uid=uid of crashed user's binary
+ * this hook runs under 0:0
+ * python hook: uid=uid of crashed user's script
+ * this hook runs under abrt:gid
+ *
+ * Currently, we set dir's gid to passwd(uid)->pw_gid parameter, and we set uid to
+ * abrt's user id. We do not allow write access to group.
+ */
+struct dump_dir *dd_create(const char *dir, uid_t uid)
+{
+ struct dump_dir *dd = dd_init();
+
+ /* Unlike dd_opendir, can't use realpath: the directory doesn't exist yet,
+ * 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);
+
+ const char *last_component = strrchr(dir, '/');
+ if (last_component)
+ last_component++;
+ else
+ last_component = dir;
+ if (dot_or_dotdot(last_component))
+ {
+ /* dd_create("."), dd_create(".."), dd_create("dir/."),
+ * dd_create("dir/..") and similar are madness, refuse them.
+ */
+ error_msg("Bad dir name '%s'", dir);
+ dd_close(dd);
+ return NULL;
+ }
+
+ dd_lock(dd);
+
+ /* 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);
+ dd_close(dd);
+ return NULL;
+ }
+
+ /* 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);
+ 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)
+ {
+ perror_msg("Can't change '%s' ownership to %lu:%lu", dir,
+ (long)dd->dd_uid, (long)dd->dd_gid);
+ }
+
+ char long_str[sizeof(long) * 3 + 2];
+
+ sprintf(long_str, "%lu", (long)uid);
+ dd_save_text(dd, CD_UID, long_str);
+
+ struct utsname buf;
+ 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);
+
+ time_t t = time(NULL);
+ sprintf(long_str, "%lu", (long)t);
+ dd_save_text(dd, FILENAME_TIME, long_str);
+
+ return dd;
+}
+
+static void delete_file_dir(const char *dir)
+{
+ DIR *d = opendir(dir);
+ if (!d)
+ return;
+
+ struct dirent *dent;
+ while ((dent = readdir(d)) != NULL)
+ {
+ if (dot_or_dotdot(dent->d_name))
+ continue;
+ char *full_path = concat_path_file(dir, dent->d_name);
+ if (unlink(full_path) == -1 && errno != ENOENT)
+ {
+ if (errno != EISDIR)
+ {
+ error_msg("Can't remove '%s'", full_path);
+ free(full_path);
+ closedir(d);
+ return;
+ }
+ delete_file_dir(full_path);
+ }
+ free(full_path);
+ }
+ closedir(d);
+ if (rmdir(dir) == -1)
+ {
+ error_msg("Can't remove dir '%s'", dir);
+ }
+}
+
+void dd_delete(struct dump_dir *dd)
+{
+ delete_file_dir(dd->dd_dir);
+}
+
+static char *load_text_file(const char *path, unsigned flags)
+{
+ FILE *fp = fopen(path, "r");
+ if (!fp)
+ {
+ if (!(flags & DD_FAIL_QUIETLY))
+ perror_msg("Can't open file '%s'", path);
+ return (flags & DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE ? NULL : xstrdup(""));
+ }
+
+ struct strbuf *buf_content = strbuf_new();
+ int ch;
+ while ((ch = fgetc(fp)) != EOF)
+ {
+ if (ch == '\0')
+ strbuf_append_char(buf_content, ' ');
+ else if (isspace(ch) || (isascii(ch) && !iscntrl(ch)))
+ strbuf_append_char(buf_content, ch);
+ }
+ fclose(fp);
+
+ return strbuf_free_nobuf(buf_content);
+}
+
+static bool save_binary_file(const char *path, const char* data, unsigned size, uid_t uid, gid_t gid)
+{
+ /* "Why 0640?!" See ::Create() for security analysis */
+ unlink(path);
+ int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0640);
+ if (fd < 0)
+ {
+ perror_msg("Can't open file '%s'", path);
+ return false;
+ }
+ 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)
+ {
+ error_msg("Can't save file '%s'", path);
+ return false;
+ }
+
+ return true;
+}
+
+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 */
+
+ char *full_path = concat_path_file(dd->dd_dir, name);
+ char *ret = load_text_file(full_path, flags);
+ free(full_path);
+
+ return ret;
+}
+
+char* dd_load_text(const struct dump_dir *dd, const char *name)
+{
+ return dd_load_text_ext(dd, name, /*flags:*/ 0);
+}
+
+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);
+ save_binary_file(full_path, data, strlen(data), dd->dd_uid, dd->dd_gid);
+ free(full_path);
+}
+
+void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, unsigned size)
+{
+ if (!dd->locked)
+ error_msg_and_die("dump_dir is not opened"); /* bug */
+
+ char *full_path = concat_path_file(dd->dd_dir, 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->next_dir)
+ closedir(dd->next_dir);
+
+ dd->next_dir = opendir(dd->dd_dir);
+ if (!dd->next_dir)
+ {
+ error_msg("Can't open dir '%s'", dd->dd_dir);
+ }
+
+ return dd->next_dir;
+}
+
+int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name)
+{
+ if (dd->next_dir == NULL)
+ return 0;
+
+ struct dirent *dent;
+ while ((dent = readdir(dd->next_dir)) != NULL)
+ {
+ if (is_regular_file(dent, dd->dd_dir))
+ {
+ if (short_name)
+ *short_name = xstrdup(dent->d_name);
+ if (full_name)
+ *full_name = concat_path_file(dd->dd_dir, dent->d_name);
+ return 1;
+ }
+ }
+
+ closedir(dd->next_dir);
+ dd->next_dir = NULL;
+ return 0;
+}
+
+/* Utility function */
+void delete_debug_dump_dir(const char *dd_dir)
+{
+ struct dump_dir *dd = dd_opendir(dd_dir, /*flags:*/ 0);
+ if (dd)
+ {
+ dd_delete(dd);
+ dd_close(dd);
+ }
+}
diff --git a/src/lib/encbase64.c b/src/lib/encbase64.c
new file mode 100644
index 00000000..6a6f1f75
--- /dev/null
+++ b/src/lib/encbase64.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include "abrtlib.h" /* xmalloc */
+
+/* Conversion table for base 64 */
+static const char tbl_base64[65 /*+ 2*/] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+ '=' /* termination character */,
+ // '\n', '\0' /* needed for uudecode.c */
+};
+
+/* Conversion table for uuencode
+const char tbl_uuencode[65] ALIGN1 = {
+ '`', '!', '"', '#', '$', '%', '&', '\'',
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ '`'
+};
+*/
+
+/*
+ * Encode bytes at S of length LENGTH.
+ * Result will be 0-terminated, and must point to a writable
+ * buffer of at least 1+BASE64_LENGTH(length) bytes,
+ * where BASE64_LENGTH(len) = 4 * ((LENGTH + 2) / 3)
+ */
+static void encode_64bit(char *p, const void *src, int length, const char *tbl)
+{
+ const unsigned char *s = (const unsigned char *)src;
+
+ /* Transform the 3x8 bits to 4x6 bits */
+ while (length > 0) {
+ unsigned s1, s2;
+
+ /* Are s[1], s[2] valid or should be assumed 0? */
+ s1 = s2 = 0;
+ length -= 3; /* can be >=0, -1, -2 */
+ if (length >= -1) {
+ s1 = s[1];
+ if (length >= 0)
+ s2 = s[2];
+ }
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
+ *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
+ *p++ = tbl[s2 & 0x3f];
+ s += 3;
+ }
+ /* Zero-terminate */
+ *p = '\0';
+ /* If length is -2 or -1, pad last char or two */
+ while (length) {
+ *--p = tbl[64];
+ length++;
+ }
+}
+
+char *encode_base64(const void *src, int length)
+{
+ char *dst = (char *)xmalloc(4 * ((length + 2) / 3) + 1);
+ encode_64bit(dst, src, length, tbl_base64);
+ return dst;
+}
diff --git a/src/lib/hash_md5.c b/src/lib/hash_md5.c
new file mode 100644
index 00000000..8bfddd54
--- /dev/null
+++ b/src/lib/hash_md5.c
@@ -0,0 +1,444 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * md5.c - Compute MD5 checksum of strings according to the
+ * definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Copyright (C) 2001 Manuel Novoa III
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+#include "abrtlib.h"
+#include <byteswap.h>
+
+#if defined(__BIG_ENDIAN__) && __BIG_ENDIAN__
+# define MD5_BIG_ENDIAN 1
+# define MD5_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define MD5_BIG_ENDIAN 1
+# define MD5_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+# define MD5_BIG_ENDIAN 0
+# define MD5_LITTLE_ENDIAN 1
+#else
+# error "Can't determine endianness"
+#endif
+
+/* SWAP_LEnn means "convert CPU<->little_endian if needed (by swapping bytes)" */
+#if MD5_BIG_ENDIAN
+# define SWAP_BE32(x) (x)
+# define SWAP_LE32(x) bswap_32(x)
+#else
+# define SWAP_BE32(x) bswap_32(x)
+# define SWAP_LE32(x) (x)
+#endif
+
+
+/* 0: fastest, 3: smallest */
+#define MD5_SIZE_VS_SPEED 3
+
+/* Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+void md5_begin(md5_ctx_t *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+ ctx->total = 0;
+ ctx->buflen = 0;
+}
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ * and defined in the RFC 1321. The first function is a little bit optimized
+ * (as found in Colin Plumbs public domain implementation).
+ * #define FF(b, c, d) ((b & c) | (~b & d))
+ */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF(d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+#define rotl32(w, s) (((w) << (s)) | ((w) >> (32 - (s))))
+
+/* Hash a single block, 64 bytes long and 4-byte aligned. */
+static void md5_hash_block(const void *buffer, md5_ctx_t *ctx)
+{
+ uint32_t correct_words[16];
+ const uint32_t *words = (const uint32_t *)buffer;
+
+#if MD5_SIZE_VS_SPEED > 0
+ static const uint32_t C_array[] = {
+ /* round 1 */
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ /* round 2 */
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ /* round 3 */
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ /* round 4 */
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+ };
+ static const char P_array[] = {
+# if MD5_SIZE_VS_SPEED > 1
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
+# endif
+ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */
+ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */
+ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */
+ };
+# if MD5_SIZE_VS_SPEED > 1
+ static const char S_array[] = {
+ 7, 12, 17, 22,
+ 5, 9, 14, 20,
+ 4, 11, 16, 23,
+ 6, 10, 15, 21
+ };
+# endif /* MD5_SIZE_VS_SPEED > 1 */
+#endif
+ uint32_t A = ctx->A;
+ uint32_t B = ctx->B;
+ uint32_t C = ctx->C;
+ uint32_t D = ctx->D;
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ uint32_t *cwp = correct_words;
+ uint32_t A_save = A;
+ uint32_t B_save = B;
+ uint32_t C_save = C;
+ uint32_t D_save = D;
+
+#if MD5_SIZE_VS_SPEED > 1
+ const uint32_t *pc;
+ const char *pp;
+ const char *ps;
+ int i;
+ uint32_t temp;
+
+ for (i = 0; i < 16; i++) {
+ cwp[i] = SWAP_LE32(words[i]);
+ }
+ words += 16;
+
+# if MD5_SIZE_VS_SPEED > 2
+ pc = C_array;
+ pp = P_array;
+ ps = S_array - 4;
+
+ for (i = 0; i < 64; i++) {
+ if ((i & 0x0f) == 0)
+ ps += 4;
+ temp = A;
+ switch (i >> 4) {
+ case 0:
+ temp += FF(B, C, D);
+ break;
+ case 1:
+ temp += FG(B, C, D);
+ break;
+ case 2:
+ temp += FH(B, C, D);
+ break;
+ case 3:
+ temp += FI(B, C, D);
+ }
+ temp += cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+# else
+ pc = C_array;
+ pp = P_array;
+ ps = S_array;
+
+ for (i = 0; i < 16; i++) {
+ temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+
+# endif /* MD5_SIZE_VS_SPEED > 2 */
+#else
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+# define OP(a, b, c, d, s, T) \
+ do { \
+ a += FF(b, c, d) + (*cwp++ = SWAP_LE32(*words)) + T; \
+ ++words; \
+ a = rotl32(a, s); \
+ a += b; \
+ } while (0)
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+ T[i] = (int)(4294967296.0 * fabs(sin(i))), i=1..64
+ */
+
+# if MD5_SIZE_VS_SPEED == 1
+ const uint32_t *pc;
+ const char *pp;
+ int i;
+# endif /* MD5_SIZE_VS_SPEED */
+
+ /* Round 1. */
+# if MD5_SIZE_VS_SPEED == 1
+ pc = C_array;
+ for (i = 0; i < 4; i++) {
+ OP(A, B, C, D, 7, *pc++);
+ OP(D, A, B, C, 12, *pc++);
+ OP(C, D, A, B, 17, *pc++);
+ OP(B, C, D, A, 22, *pc++);
+ }
+# else
+ OP(A, B, C, D, 7, 0xd76aa478);
+ OP(D, A, B, C, 12, 0xe8c7b756);
+ OP(C, D, A, B, 17, 0x242070db);
+ OP(B, C, D, A, 22, 0xc1bdceee);
+ OP(A, B, C, D, 7, 0xf57c0faf);
+ OP(D, A, B, C, 12, 0x4787c62a);
+ OP(C, D, A, B, 17, 0xa8304613);
+ OP(B, C, D, A, 22, 0xfd469501);
+ OP(A, B, C, D, 7, 0x698098d8);
+ OP(D, A, B, C, 12, 0x8b44f7af);
+ OP(C, D, A, B, 17, 0xffff5bb1);
+ OP(B, C, D, A, 22, 0x895cd7be);
+ OP(A, B, C, D, 7, 0x6b901122);
+ OP(D, A, B, C, 12, 0xfd987193);
+ OP(C, D, A, B, 17, 0xa679438e);
+ OP(B, C, D, A, 22, 0x49b40821);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+# undef OP
+# define OP(f, a, b, c, d, k, s, T) \
+ do { \
+ a += f(b, c, d) + correct_words[k] + T; \
+ a = rotl32(a, s); \
+ a += b; \
+ } while (0)
+
+ /* Round 2. */
+# if MD5_SIZE_VS_SPEED == 1
+ pp = P_array;
+ for (i = 0; i < 4; i++) {
+ OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
+ OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
+ OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
+ OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
+ }
+# else
+ OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP(FG, D, A, B, C, 10, 9, 0x02441453);
+ OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* Round 3. */
+# if MD5_SIZE_VS_SPEED == 1
+ for (i = 0; i < 4; i++) {
+ OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
+ OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
+ OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
+ OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
+ }
+# else
+ OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* Round 4. */
+# if MD5_SIZE_VS_SPEED == 1
+ for (i = 0; i < 4; i++) {
+ OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
+ OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
+ OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
+ OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
+ }
+# else
+ OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+#endif /* MD5_SIZE_VS_SPEED > 1 */
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+/* Feed data through a temporary buffer to call md5_hash_aligned_block()
+ * with chunks of data that are 4-byte aligned and a multiple of 64 bytes.
+ * This function's internal buffer remembers previous data until it has 64
+ * bytes worth to pass on. Call md5_end() to flush this buffer. */
+void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx)
+{
+ char *buf = (char *)buffer;
+
+ /* RFC 1321 specifies the possible length of the file up to 2^64 bits,
+ * Here we only track the number of bytes. */
+ ctx->total += len;
+
+ /* Process all input. */
+ while (len) {
+ unsigned i = 64 - ctx->buflen;
+
+ /* Copy data into aligned buffer. */
+ if (i > len) i = len;
+ memcpy(ctx->buffer + ctx->buflen, buf, i);
+ len -= i;
+ ctx->buflen += i;
+ buf += i;
+
+ /* When buffer fills up, process it. */
+ if (ctx->buflen == 64) {
+ md5_hash_block(ctx->buffer, ctx);
+ ctx->buflen = 0;
+ }
+ }
+}
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ * in first 16 bytes following RESBUF. The result is always in little
+ * endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ */
+void md5_end(void *resbuf, md5_ctx_t *ctx)
+{
+ char *buf = ctx->buffer;
+ int i;
+
+ /* Pad data to block size. */
+ buf[ctx->buflen++] = 0x80;
+ memset(buf + ctx->buflen, 0, 128 - ctx->buflen);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ ctx->total <<= 3;
+ if (ctx->buflen > 56)
+ buf += 64;
+ for (i = 0; i < 8; i++)
+ buf[56 + i] = ctx->total >> (i*8);
+
+ /* Process last bytes. */
+ if (buf != ctx->buffer)
+ md5_hash_block(ctx->buffer, ctx);
+ md5_hash_block(buf, ctx);
+
+ /* The MD5 result is in little endian byte order.
+ * We (ab)use the fact that A-D are consecutive in memory.
+ */
+#if MD5_BIG_ENDIAN
+ ctx->A = SWAP_LE32(ctx->A);
+ ctx->B = SWAP_LE32(ctx->B);
+ ctx->C = SWAP_LE32(ctx->C);
+ ctx->D = SWAP_LE32(ctx->D);
+#endif
+ memcpy(resbuf, &ctx->A, sizeof(ctx->A) * 4);
+}
diff --git a/src/lib/hash_md5.h b/src/lib/hash_md5.h
new file mode 100644
index 00000000..cc1d2c43
--- /dev/null
+++ b/src/lib/hash_md5.h
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * md5.c - Compute MD5 checksum of strings according to the
+ * definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Copyright (C) 2001 Manuel Novoa III
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#define MD5_RESULT_LEN 16
+
+typedef struct md5_ctx_t {
+ uint32_t A;
+ uint32_t B;
+ uint32_t C;
+ uint32_t D;
+ uint64_t total;
+ uint32_t buflen;
+ char buffer[128];
+} md5_ctx_t;
+void md5_begin(md5_ctx_t *ctx);
+void md5_hash(const void *data, size_t length, md5_ctx_t *ctx);
+void md5_end(void *resbuf, md5_ctx_t *ctx);
diff --git a/src/lib/hash_sha1.c b/src/lib/hash_sha1.c
new file mode 100644
index 00000000..954e7bc8
--- /dev/null
+++ b/src/lib/hash_sha1.c
@@ -0,0 +1,213 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Based on shasum from http://www.netsw.org/crypto/hash/
+ * Majorly hacked up to use Dr Brian Gladman's sha1 code
+ *
+ * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * Issue Date: 10/11/2002
+ *
+ * This is a byte oriented version of SHA1 that operates on arrays of bytes
+ * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
+ *
+ * ---------------------------------------------------------------------------
+ */
+#include "abrtlib.h"
+
+#if defined(__BIG_ENDIAN__) && __BIG_ENDIAN__
+# define SHA1_BIG_ENDIAN 1
+# define SHA1_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define SHA1_BIG_ENDIAN 1
+# define SHA1_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+# define SHA1_BIG_ENDIAN 0
+# define SHA1_LITTLE_ENDIAN 1
+#else
+# error "Can't determine endianness"
+#endif
+
+
+#define rotl32(x,n) (((x) << (n)) | ((x) >> (32 - (n))))
+#define rotr32(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
+/* for sha512: */
+#define rotr64(x,n) (((x) >> (n)) | ((x) << (64 - (n))))
+#if SHA1_LITTLE_ENDIAN
+static inline uint64_t hton64(uint64_t v)
+{
+ return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
+}
+#else
+#define hton64(v) (v)
+#endif
+#define ntoh64(v) hton64(v)
+
+/* To check alignment gcc has an appropriate operator. Other
+ compilers don't. */
+#if defined(__GNUC__) && __GNUC__ >= 2
+# define UNALIGNED_P(p,type) (((uintptr_t) p) % __alignof__(type) != 0)
+#else
+# define UNALIGNED_P(p,type) (((uintptr_t) p) % sizeof(type) != 0)
+#endif
+
+
+/* Some arch headers have conflicting defines */
+#undef ch
+#undef parity
+#undef maj
+#undef rnd
+
+static void sha1_process_block64(sha1_ctx_t *ctx)
+{
+ unsigned t;
+ uint32_t W[80], a, b, c, d, e;
+ const uint32_t *words = (uint32_t*) ctx->wbuffer;
+
+ for (t = 0; t < 16; ++t) {
+ W[t] = ntohl(*words);
+ words++;
+ }
+
+ for (/*t = 16*/; t < 80; ++t) {
+ uint32_t T = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16];
+ W[t] = rotl32(T, 1);
+ }
+
+ a = ctx->hash[0];
+ b = ctx->hash[1];
+ c = ctx->hash[2];
+ d = ctx->hash[3];
+ e = ctx->hash[4];
+
+/* Reverse byte order in 32-bit words */
+#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
+#define parity(x,y,z) ((x) ^ (y) ^ (z))
+#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
+/* A normal version as set out in the FIPS. This version uses */
+/* partial loop unrolling and is optimised for the Pentium 4 */
+#define rnd(f,k) \
+ do { \
+ uint32_t T = a; \
+ a = rotl32(a, 5) + f(b, c, d) + e + k + W[t]; \
+ e = d; \
+ d = c; \
+ c = rotl32(b, 30); \
+ b = T; \
+ } while (0)
+
+ for (t = 0; t < 20; ++t)
+ rnd(ch, 0x5a827999);
+
+ for (/*t = 20*/; t < 40; ++t)
+ rnd(parity, 0x6ed9eba1);
+
+ for (/*t = 40*/; t < 60; ++t)
+ rnd(maj, 0x8f1bbcdc);
+
+ for (/*t = 60*/; t < 80; ++t)
+ rnd(parity, 0xca62c1d6);
+#undef ch
+#undef parity
+#undef maj
+#undef rnd
+
+ ctx->hash[0] += a;
+ ctx->hash[1] += b;
+ ctx->hash[2] += c;
+ ctx->hash[3] += d;
+ ctx->hash[4] += e;
+}
+
+void sha1_begin(sha1_ctx_t *ctx)
+{
+ ctx->hash[0] = 0x67452301;
+ ctx->hash[1] = 0xefcdab89;
+ ctx->hash[2] = 0x98badcfe;
+ ctx->hash[3] = 0x10325476;
+ ctx->hash[4] = 0xc3d2e1f0;
+ ctx->total64 = 0;
+ ctx->process_block = sha1_process_block64;
+}
+
+static const uint32_t init256[] = {
+ 0x6a09e667,
+ 0xbb67ae85,
+ 0x3c6ef372,
+ 0xa54ff53a,
+ 0x510e527f,
+ 0x9b05688c,
+ 0x1f83d9ab,
+ 0x5be0cd19
+};
+static const uint32_t init512_lo[] = {
+ 0xf3bcc908,
+ 0x84caa73b,
+ 0xfe94f82b,
+ 0x5f1d36f1,
+ 0xade682d1,
+ 0x2b3e6c1f,
+ 0xfb41bd6b,
+ 0x137e2179
+};
+
+/* Used also for sha256 */
+void sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx)
+{
+ unsigned in_buf = ctx->total64 & 63;
+ unsigned add = 64 - in_buf;
+
+ ctx->total64 += len;
+
+ while (len >= add) { /* transfer whole blocks while possible */
+ memcpy(ctx->wbuffer + in_buf, buffer, add);
+ buffer = (const char *)buffer + add;
+ len -= add;
+ add = 64;
+ in_buf = 0;
+ ctx->process_block(ctx);
+ }
+
+ memcpy(ctx->wbuffer + in_buf, buffer, len);
+}
+
+/* Used also for sha256 */
+void sha1_end(void *resbuf, sha1_ctx_t *ctx)
+{
+ unsigned pad, in_buf;
+
+ in_buf = ctx->total64 & 63;
+ /* Pad the buffer to the next 64-byte boundary with 0x80,0,0,0... */
+ ctx->wbuffer[in_buf++] = 0x80;
+
+ /* This loop iterates either once or twice, no more, no less */
+ while (1) {
+ pad = 64 - in_buf;
+ memset(ctx->wbuffer + in_buf, 0, pad);
+ in_buf = 0;
+ /* Do we have enough space for the length count? */
+ if (pad >= 8) {
+ /* Store the 64-bit counter of bits in the buffer in BE format */
+ uint64_t t = ctx->total64 << 3;
+ t = hton64(t);
+ /* wbuffer is suitably aligned for this */
+ *(uint64_t *) (&ctx->wbuffer[64 - 8]) = t;
+ }
+ ctx->process_block(ctx);
+ if (pad >= 8)
+ break;
+ }
+
+ in_buf = (ctx->process_block == sha1_process_block64) ? 5 : 8;
+ /* This way we do not impose alignment constraints on resbuf: */
+ if (SHA1_LITTLE_ENDIAN) {
+ unsigned i;
+ for (i = 0; i < in_buf; ++i)
+ ctx->hash[i] = htonl(ctx->hash[i]);
+ }
+ memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * in_buf);
+}
diff --git a/src/lib/hash_sha1.h b/src/lib/hash_sha1.h
new file mode 100644
index 00000000..02978ea4
--- /dev/null
+++ b/src/lib/hash_sha1.h
@@ -0,0 +1,40 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Based on shasum from http://www.netsw.org/crypto/hash/
+ * Majorly hacked up to use Dr Brian Gladman's sha1 code
+ *
+ * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * Issue Date: 10/11/2002
+ *
+ * This is a byte oriented version of SHA1 that operates on arrays of bytes
+ * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
+ *
+ * ---------------------------------------------------------------------------
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHA1_RESULT_LEN (5 * 4)
+
+typedef struct sha1_ctx_t {
+ uint32_t hash[8]; /* 5, +3 elements for sha256 */
+ uint64_t total64;
+ uint8_t wbuffer[64]; /* NB: always correctly aligned for uint64_t */
+ void (*process_block)(struct sha1_ctx_t*);
+} sha1_ctx_t;
+
+void sha1_begin(sha1_ctx_t *ctx);
+void sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx);
+void sha1_end(void *resbuf, sha1_ctx_t *ctx);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/lib/hooklib.c b/src/lib/hooklib.c
new file mode 100644
index 00000000..062e3ddb
--- /dev/null
+++ b/src/lib/hooklib.c
@@ -0,0 +1,138 @@
+/*
+ Copyright (C) 2009 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"
+#include "hooklib.h"
+#include <sys/statvfs.h>
+
+void parse_conf(const char *additional_conf, unsigned *setting_MaxCrashReportsSize, bool *setting_MakeCompatCore, bool *setting_SaveBinaryImage)
+{
+ FILE *fp = fopen(CONF_DIR"/abrt.conf", "r");
+ if (!fp)
+ return;
+
+ while (1)
+ {
+ char *line = xmalloc_fgetline(fp);
+ if (!line)
+ {
+ fclose(fp);
+ if (additional_conf)
+ {
+ /* Next .conf file plz */
+ fp = fopen(additional_conf, "r");
+ if (fp)
+ {
+ additional_conf = NULL;
+ continue;
+ }
+ }
+ break;
+ }
+
+ const char *p = skip_whitespace(line);
+#undef DIRECTIVE
+#define DIRECTIVE "MaxCrashReportsSize"
+ if (setting_MaxCrashReportsSize && strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)
+ {
+ p = skip_whitespace(p + sizeof(DIRECTIVE)-1);
+ if (*p != '=')
+ goto free_line;
+ p = skip_whitespace(p + 1);
+ if (isdigit(*p))
+ {
+ /* x1.25: go a bit up, so that usual in-daemon trimming
+ * kicks in first, and we don't "fight" with it. */
+ *setting_MaxCrashReportsSize = (unsigned long)xatou(p) * 5 / 4;
+ }
+ goto free_line;
+ }
+#undef DIRECTIVE
+#define DIRECTIVE "MakeCompatCore"
+ if (setting_MakeCompatCore && strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)
+ {
+ p = skip_whitespace(p + sizeof(DIRECTIVE)-1);
+ if (*p != '=')
+ goto free_line;
+ p = skip_whitespace(p + 1);
+ *setting_MakeCompatCore = string_to_bool(p);
+ goto free_line;
+ }
+#undef DIRECTIVE
+#define DIRECTIVE "SaveBinaryImage"
+ if (setting_SaveBinaryImage && strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)
+ {
+ p = skip_whitespace(p + sizeof(DIRECTIVE)-1);
+ if (*p != '=')
+ goto free_line;
+ p = skip_whitespace(p + 1);
+ *setting_SaveBinaryImage = string_to_bool(p);
+ goto free_line;
+ }
+#undef DIRECTIVE
+ /* add more 'if (strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)' here... */
+
+ free_line:
+ free(line);
+ }
+}
+
+void check_free_space(unsigned setting_MaxCrashReportsSize)
+{
+ struct statvfs vfs;
+ if (statvfs(DEBUG_DUMPS_DIR, &vfs) != 0)
+ {
+ perror_msg_and_die("statvfs('%s')", DEBUG_DUMPS_DIR);
+ }
+
+ /* Check that at least MaxCrashReportsSize/4 MBs are free */
+
+ /* fs_free_mb_x4 ~= vfs.f_bfree * vfs.f_bsize * 4, expressed in MBytes.
+ * Need to neither overflow nor round f_bfree down too much. */
+ unsigned long fs_free_mb_x4 = ((unsigned long long)vfs.f_bfree / (1024/4)) * vfs.f_bsize / 1024;
+ if (fs_free_mb_x4 < setting_MaxCrashReportsSize)
+ {
+ error_msg_and_die("aborting dump: only %luMiB is available on %s", fs_free_mb_x4 / 4, DEBUG_DUMPS_DIR);
+ }
+}
+
+/* rhbz#539551: "abrt going crazy when crashing process is respawned".
+ * Check total size of dump dir, if it overflows,
+ * delete oldest/biggest dumps.
+ */
+void trim_debug_dumps(unsigned setting_MaxCrashReportsSize, const char *exclude_path)
+{
+ int count = 10;
+ while (--count >= 0)
+ {
+ const char *base_dirname = strrchr(exclude_path, '/') + 1; /* never NULL */
+ /* We exclude our own dump from candidates for deletion (3rd param): */
+ char *worst_dir = NULL;
+ double dirsize = get_dirsize_find_largest_dir(DEBUG_DUMPS_DIR, &worst_dir, base_dirname);
+ if (dirsize / (1024*1024) < setting_MaxCrashReportsSize || !worst_dir)
+ {
+ free(worst_dir);
+ break;
+ }
+ log("size of '%s' >= %u MB, deleting '%s'", DEBUG_DUMPS_DIR, setting_MaxCrashReportsSize, worst_dir);
+ char *d = concat_path_file(DEBUG_DUMPS_DIR, worst_dir);
+ free(worst_dir);
+ worst_dir = NULL;
+ delete_debug_dump_dir(d);
+ free(d);
+ }
+}
diff --git a/src/lib/hooklib.h b/src/lib/hooklib.h
new file mode 100644
index 00000000..ba76efbc
--- /dev/null
+++ b/src/lib/hooklib.h
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2009 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.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void parse_conf(const char *additional_conf, unsigned *setting_MaxCrashReportsSize, bool *setting_MakeCompatCore, bool *setting_SaveBinaryImage);
+void check_free_space(unsigned setting_MaxCrashReportsSize);
+void trim_debug_dumps(unsigned setting_MaxCrashReportsSize, const char *exclude_path);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/lib/logging.c b/src/lib/logging.c
new file mode 100644
index 00000000..815efb5e
--- /dev/null
+++ b/src/lib/logging.c
@@ -0,0 +1,148 @@
+/*
+ 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.
+*/
+
+/*
+ * Utility routines.
+ *
+ */
+#include "abrtlib.h"
+
+void (*g_custom_logger)(const char*);
+const char *msg_prefix = "";
+const char *msg_eol = "\n";
+int logmode = LOGMODE_STDIO;
+int xfunc_error_retval = EXIT_FAILURE;
+int g_verbose;
+
+void xfunc_die(void)
+{
+ exit(xfunc_error_retval);
+}
+
+static void verror_msg_helper(const char *s,
+ va_list p,
+ const char* strerr,
+ int flags)
+{
+ char *msg;
+ int prefix_len, strerr_len, msgeol_len, used;
+
+ if (!logmode)
+ return;
+
+ used = vasprintf(&msg, s, p);
+ if (used < 0)
+ return;
+
+ /* This is ugly and costs +60 bytes compared to multiple
+ * fprintf's, but is guaranteed to do a single write.
+ * This is needed for e.g. when multiple children
+ * can produce log messages simultaneously. */
+
+ prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
+ strerr_len = strerr ? strlen(strerr) : 0;
+ msgeol_len = strlen(msg_eol);
+ /* +3 is for ": " before strerr and for terminating NUL */
+ msg = (char*) xrealloc(msg, prefix_len + used + strerr_len + msgeol_len + 3);
+ /* TODO: maybe use writev instead of memmoving? Need full_writev? */
+ if (prefix_len) {
+ char *p;
+ memmove(msg + prefix_len, msg, used);
+ used += prefix_len;
+ p = stpcpy(msg, msg_prefix);
+ p[0] = ':';
+ p[1] = ' ';
+ }
+ if (strerr) {
+ if (s[0]) {
+ msg[used++] = ':';
+ msg[used++] = ' ';
+ }
+ strcpy(&msg[used], strerr);
+ used += strerr_len;
+ }
+ strcpy(&msg[used], msg_eol);
+
+ if (flags & LOGMODE_STDIO) {
+ fflush(stdout);
+ full_write(STDERR_FILENO, msg, used + msgeol_len);
+ }
+ msg[used] = '\0'; /* remove msg_eol (usually "\n") */
+ if (flags & LOGMODE_SYSLOG) {
+ syslog(LOG_ERR, "%s", msg + prefix_len);
+ }
+ if ((flags & LOGMODE_CUSTOM) && g_custom_logger) {
+ g_custom_logger(msg + prefix_len);
+ }
+ free(msg);
+}
+
+void log_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg_helper(s, p, NULL, logmode);
+ va_end(p);
+}
+
+void error_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg_helper(s, p, NULL, (logmode | LOGMODE_CUSTOM));
+ va_end(p);
+}
+
+void error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg_helper(s, p, NULL, (logmode | LOGMODE_CUSTOM));
+ va_end(p);
+ xfunc_die();
+}
+
+void perror_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ /* Guard against "<error message>: Success" */
+ verror_msg_helper(s, p, errno ? strerror(errno) : NULL, (logmode | LOGMODE_CUSTOM));
+ va_end(p);
+}
+
+void perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ /* Guard against "<error message>: Success" */
+ verror_msg_helper(s, p, errno ? strerror(errno) : NULL, (logmode | LOGMODE_CUSTOM));
+ va_end(p);
+ xfunc_die();
+}
+
+void die_out_of_memory(void)
+{
+ error_msg_and_die("Out of memory, exiting");
+}
diff --git a/src/lib/logging.h b/src/lib/logging.h
new file mode 100644
index 00000000..8a038bc7
--- /dev/null
+++ b/src/lib/logging.h
@@ -0,0 +1,78 @@
+/*
+ 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 LOGGING_H
+#define LOGGING_H
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/syslog.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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),
+ LOGMODE_SYSLOG = (1 << 1),
+ LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO,
+ LOGMODE_CUSTOM = (1 << 2),
+};
+
+extern void (*g_custom_logger)(const char*);
+extern const char *msg_prefix;
+extern const char *msg_eol;
+extern int logmode;
+extern int xfunc_error_retval;
+
+/* Verbosity level */
+extern int g_verbose;
+
+void xfunc_die(void) NORETURN;
+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. */
+void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+/* Reports error message with libc's errno error description attached. */
+void perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+void perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+void die_out_of_memory(void) NORETURN;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/make_descr.cpp b/src/lib/make_descr.cpp
new file mode 100644
index 00000000..79065100
--- /dev/null
+++ b/src/lib/make_descr.cpp
@@ -0,0 +1,287 @@
+/*
+ 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"
+#include "crash_types.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)
+{
+ struct strbuf *buf_description = strbuf_new();
+ if (*was_multiline)
+ strbuf_append_char(buf_description, '\n');
+
+ while (content[0] == '\n')
+ content++;
+
+ if (strchr(content, '\n') == NULL)
+ {
+ if (skip_whitespace(content)[0] == '\0')
+ {
+ /* empty, dont report at all */
+ *dsc = strbuf_free_nobuf(buf_description);
+ return;
+ }
+ /* one string value, like OS release */
+ strbuf_append_strf(buf_description, "%s: %s\n", header, content);
+ *was_multiline = 0;
+ }
+ else
+ {
+ /* multi-string value, like backtrace */
+ if (!*was_multiline && (buf_description->len != 0)) /* if wasn't yet separated */
+ strbuf_append_char(buf_description, '\n');
+
+ strbuf_append_strf(buf_description, "%s\n-----\n%s", header, content);
+ if (content[strlen(content) - 1] != '\n')
+ strbuf_append_char(buf_description, '\n');
+
+ *was_multiline = 1;
+ }
+
+ *dsc = strbuf_free_nobuf(buf_description);
+}
+
+/* Items we don't want to include */
+static const char *const blacklisted_items[] = {
+ FILENAME_ANALYZER ,
+ FILENAME_COREDUMP ,
+ FILENAME_DESCRIPTION, /* package description - basically useless */
+ FILENAME_HOSTNAME ,
+ FILENAME_DUPHASH ,
+ CD_UUID ,
+ CD_INFORMALL ,
+ FILENAME_DUPHASH ,
+ CD_DUMPDIR ,
+ CD_COUNT ,
+ CD_REPORTED ,
+ CD_MESSAGE ,
+ NULL
+};
+
+char* make_dsc_mailx(const map_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++)
+ {
+ if (it->second[CD_TYPE] == CD_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)
+ ) {
+ strbuf_append_strf(buf_additional_files, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str());
+ }
+ 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
+ strbuf_append_strf(buf_common_files, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str());
+ }
+ }
+
+ char *common_files = strbuf_free_nobuf(buf_common_files);
+ char *duphash_file = strbuf_free_nobuf(buf_duphash_file);
+ char *additional_files = strbuf_free_nobuf(buf_additional_files);
+
+ strbuf_append_strf(buf_dsc, "Duplicate check\n=====\n%s\n\n", duphash_file);
+ strbuf_append_strf(buf_dsc, "Common information\n=====\n%s\n\n", common_files);
+ strbuf_append_strf(buf_dsc, "Additional information\n=====\n%s\n", additional_files);
+
+ free(common_files);
+ free(duphash_file);
+ free(additional_files);
+
+ return strbuf_free_nobuf(buf_dsc);
+}
+
+char* make_description_bz(const map_crash_data_t& pCrashData)
+{
+ 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++)
+ {
+ 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)
+ {
+ /* Skip items we are not interested in */
+ const char *const *bl = blacklisted_items;
+ while (*bl)
+ {
+ if (strcmp(itemname, *bl) == 0)
+ break;
+ bl++;
+ }
+ if (*bl)
+ continue; /* blacklisted */
+ if (strcmp(content, "1.\n2.\n3.\n") == 0)
+ continue; /* user did not change default "How to reproduce" */
+
+ if (strlen(content) <= CD_TEXT_ATT_SIZE)
+ {
+ /* Add small (less than few kb) text items inline */
+ bool was_multiline = 0;
+ char *tmp = NULL;
+ add_content(&was_multiline,
+ &tmp,
+ /* "reproduce: blah" looks ugly, fixing: */
+ (strcmp(itemname, FILENAME_REPRODUCE) == 0) ? "How to reproduce" : itemname,
+ content
+ );
+
+ if (was_multiline)
+ {
+ /* Not one-liner */
+ if (buf_long_dsc->len != 0)
+ strbuf_append_char(buf_long_dsc, '\n');
+
+ strbuf_append_str(buf_long_dsc, tmp);
+ }
+ else
+ 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);
+ }
+ }
+ }
+
+ /* One-liners go first, then multi-line items */
+ if (buf_dsc->len != 0 && buf_long_dsc->len != 0)
+ strbuf_append_char(buf_dsc, '\n');
+
+
+ char *long_dsc = strbuf_free_nobuf(buf_long_dsc);
+ strbuf_append_str(buf_dsc, long_dsc);
+ free(long_dsc);
+
+ return strbuf_free_nobuf(buf_dsc);
+}
+
+char* make_description_logger(const map_crash_data_t& pCrashData)
+{
+ 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++)
+ {
+ 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)
+ ) {
+ /* Skip items we are not interested in */
+ const char *const *bl = blacklisted_items;
+ while (*bl)
+ {
+ if (filename == *bl)
+ break;
+ bl++;
+ }
+ if (*bl)
+ continue; /* blacklisted */
+ if (strcmp(content, "1.\n2.\n3.\n") == 0)
+ continue; /* user did not change default "How to reproduce" */
+
+ bool was_multiline = 0;
+ char *tmp = NULL;
+ add_content(&was_multiline, &tmp, filename, content);
+
+ if (was_multiline)
+ {
+ if (buf_long_dsc->len != 0)
+ strbuf_append_char(buf_long_dsc,'\n');
+
+ strbuf_append_str(buf_long_dsc, tmp);
+ }
+ else
+ strbuf_append_str(buf_dsc, tmp);
+ }
+ }
+
+ if (buf_dsc->len != 0 && buf_long_dsc->len != 0)
+ strbuf_append_char(buf_dsc, '\n');
+
+ char *long_dsc = strbuf_free_nobuf(buf_long_dsc);
+ strbuf_append_str(buf_dsc, long_dsc);
+ free(long_dsc);
+
+ return strbuf_free_nobuf(buf_dsc);
+}
+
+char* make_description_reproduce_comment(const map_crash_data_t& pCrashData)
+{
+ char *repro = NULL;
+ char *comment = NULL;
+
+ map_crash_data_t::const_iterator end = pCrashData.end();
+ map_crash_data_t::const_iterator it;
+
+ it = pCrashData.find(FILENAME_REPRODUCE);
+ if (it != end)
+ {
+ 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());
+ }
+ }
+
+ it = pCrashData.find(FILENAME_COMMENT);
+ if (it != end)
+ {
+ if (it->second[CD_CONTENT].size() > 0)
+ comment = xasprintf("\n\nComment\n-----\n%s", it->second[CD_CONTENT].c_str());
+ }
+
+ if (!repro && !comment)
+ return NULL;
+
+ struct strbuf *buf_dsc = strbuf_new();
+
+ if (repro)
+ strbuf_append_str(buf_dsc, repro);
+
+ if (comment)
+ strbuf_append_str(buf_dsc, comment);
+
+ free(repro);
+ free(comment);
+
+ return strbuf_free_nobuf(buf_dsc);
+}
diff --git a/src/lib/numtoa.cpp b/src/lib/numtoa.cpp
new file mode 100644
index 00000000..061da553
--- /dev/null
+++ b/src/lib/numtoa.cpp
@@ -0,0 +1,34 @@
+/*
+ Number to string conversions
+
+ 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"
+
+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 buf[sizeof(x)*3];
+ sprintf(buf, "%lld", x);
+ return buf;
+}
diff --git a/src/lib/overlapping_strcpy.c b/src/lib/overlapping_strcpy.c
new file mode 100644
index 00000000..41c1c1a1
--- /dev/null
+++ b/src/lib/overlapping_strcpy.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "abrtlib.h"
+
+/* Like strcpy but can copy overlapping strings. */
+void overlapping_strcpy(char *dst, const char *src)
+{
+ /* Cheap optimization for dst == src case -
+ * better to have it here than in many callers.
+ */
+ if (dst != src)
+ {
+ while ((*dst = *src) != '\0')
+ {
+ dst++;
+ src++;
+ }
+ }
+}
diff --git a/src/lib/parse_options.c b/src/lib/parse_options.c
new file mode 100644
index 00000000..c1a2c297
--- /dev/null
+++ b/src/lib/parse_options.c
@@ -0,0 +1,182 @@
+
+#include <getopt.h>
+#include "abrtlib.h"
+#include "parse_options.h"
+
+#define USAGE_OPTS_WIDTH 24
+#define USAGE_GAP 2
+
+void parse_usage_and_die(const char *usage, const struct options *opt)
+{
+ fprintf(stderr, _("Usage: %s\n"), usage);
+
+ if (opt->type != OPTION_GROUP)
+ 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);
+
+ if (opt->short_name && opt->long_name)
+ pos += fprintf(stderr, ", ");
+
+ if (opt->long_name)
+ pos += fprintf(stderr, "--%s", opt->long_name);
+
+ if (opt->argh)
+ {
+ const char *fmt = " %s";
+ if (opt->type == OPTION_OPTSTRING)
+ fmt = "[%s]";
+ pos += fprintf(stderr, fmt, opt->argh);
+ }
+
+ if (pos <= USAGE_OPTS_WIDTH)
+ pad = USAGE_OPTS_WIDTH - pos;
+ else
+ {
+ fputc('\n', stderr);
+ pad = USAGE_OPTS_WIDTH;
+ }
+ fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opt->help);
+ }
+ fputc('\n', stderr);
+ exit(1);
+}
+
+static int parse_opt_size(const struct options *opt)
+{
+ unsigned size = 0;
+ for (; opt->type != OPTION_END; opt++)
+ size++;
+
+ return size;
+}
+
+unsigned parse_opts(int argc, char **argv, const struct options *opt,
+ const char *usage)
+{
+ int help = 0;
+ int size = parse_opt_size(opt);
+
+ struct strbuf *shortopts = strbuf_new();
+
+ struct option *longopts = xzalloc(sizeof(longopts[0]) * (size+2));
+ struct option *curopt = longopts;
+ int ii;
+ for (ii = 0; ii < size; ++ii)
+ {
+ curopt->name = opt[ii].long_name;
+ /*curopt->flag = 0; - xzalloc did it */
+ curopt->val = opt[ii].short_name;
+
+ switch (opt[ii].type)
+ {
+ case OPTION_BOOL:
+ curopt->has_arg = no_argument;
+ if (opt[ii].short_name)
+ strbuf_append_char(shortopts, opt[ii].short_name);
+ break;
+ case OPTION_INTEGER:
+ case OPTION_STRING:
+ curopt->has_arg = required_argument;
+ if (opt[ii].short_name)
+ strbuf_append_strf(shortopts, "%c:", opt[ii].short_name);
+ break;
+ case OPTION_OPTSTRING:
+ curopt->has_arg = optional_argument;
+ if (opt[ii].short_name)
+ strbuf_append_strf(shortopts, "%c::", opt[ii].short_name);
+ break;
+ case OPTION_GROUP:
+ case OPTION_END:
+ break;
+ }
+ //log("curopt[%d].name:'%s' .has_arg:%d .flag:%p .val:%d", (int)(curopt-longopts),
+ // curopt->name, curopt->has_arg, curopt->flag, curopt->val);
+ /*
+ * getopt_long() thinks that NULL name marks the end of longopts.
+ * Example:
+ * [0] name:'verbose' val:'v'
+ * [1] name:NULL val:'c'
+ * [2] name:'force' val:'f'
+ * ... ... ...
+ * In this case, --force won't be accepted!
+ * Therefore we can only advance if name is not NULL.
+ */
+ if (curopt->name)
+ curopt++;
+ }
+ curopt->name = "help";
+ curopt->has_arg = no_argument;
+ curopt->flag = &help;
+ curopt->val = 1;
+ /* xzalloc did it already:
+ curopt++;
+ curopt->name = NULL;
+ curopt->has_arg = 0;
+ curopt->flag = NULL;
+ curopt->val = 0;
+ */
+
+ unsigned retval = 0;
+ while (1)
+ {
+ int c = getopt_long(argc, argv, shortopts->buf, longopts, NULL);
+
+ if (c == -1)
+ break;
+
+ if (c == '?' || help)
+ {
+ free(longopts);
+ strbuf_free(shortopts);
+ parse_usage_and_die(usage, opt);
+ }
+
+ for (ii = 0; ii < size; ++ii)
+ {
+ if (opt[ii].short_name == c)
+ {
+ if (ii < sizeof(retval)*8)
+ retval |= (1 << ii);
+
+ if (opt[ii].value != NULL) switch (opt[ii].type)
+ {
+ case OPTION_BOOL:
+ *(int*)opt[ii].value += 1;
+ break;
+ case OPTION_INTEGER:
+ *(int*)opt[ii].value = xatoi(optarg);
+ break;
+ case OPTION_STRING:
+ case OPTION_OPTSTRING:
+ if (optarg)
+ *(char**)opt[ii].value = (char*)optarg;
+ break;
+ case OPTION_GROUP:
+ case OPTION_END:
+ break;
+ }
+ }
+ }
+ }
+
+ free(longopts);
+ strbuf_free(shortopts);
+
+ return retval;
+}
diff --git a/src/lib/parse_options.h b/src/lib/parse_options.h
new file mode 100644
index 00000000..105f081c
--- /dev/null
+++ b/src/lib/parse_options.h
@@ -0,0 +1,53 @@
+
+#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_END,
+};
+
+struct options {
+ enum parse_opt_type type;
+ int short_name;
+ const char *long_name;
+ void *value;
+ const char *argh;
+ const char *help;
+};
+
+/*
+ * s - short_name
+ * l - long_name
+ * v - value
+ * a - argh argument help
+ * 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_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__VERBOSE(v) OPT_BOOL('v', "verbose", (v), "be verbose")
+
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/parse_release.cpp b/src/lib/parse_release.cpp
new file mode 100644
index 00000000..f9057bfe
--- /dev/null
+++ b/src/lib/parse_release.cpp
@@ -0,0 +1,59 @@
+/*
+ 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"
+
+// caller is reposible for freeing *product* and *version*
+void parse_release(const char *release, char** product, char** version)
+{
+ if (strstr(release, "Rawhide"))
+ {
+ *product = xstrdup("Fedora");
+ *version = xstrdup("rawhide");
+ VERB3 log("%s: version:'%s' product:'%s'", __func__, *version, *product);
+ return;
+ }
+
+ struct strbuf *buf_product = strbuf_new();
+ 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 ");
+
+ const char *r = strstr(release, "release");
+ const char *space = r ? strchr(r, ' ') : NULL;
+
+ struct strbuf *buf_version = strbuf_new();
+ if (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))
+ strbuf_append_char(buf_product, *space);
+
+ space++;
+ }
+ }
+
+ *version = strbuf_free_nobuf(buf_version);
+ *product = strbuf_free_nobuf(buf_product);
+
+ VERB3 log("%s: version:'%s' product:'%s'", __func__, *version, *product);
+}
diff --git a/src/lib/read_write.c b/src/lib/read_write.c
new file mode 100644
index 00000000..da067f78
--- /dev/null
+++ b/src/lib/read_write.c
@@ -0,0 +1,105 @@
+/*
+ * Utility routines.
+ *
+ * Licensed under GPLv2 or later, see file COPYING in this tarball for details.
+ */
+#include "abrtlib.h"
+
+/* Die with an error message if we can't read the entire buffer. */
+void xread(int fd, void *buf, size_t count)
+{
+ if (count)
+ {
+ ssize_t size = full_read(fd, buf, count);
+ if ((size_t)size != count)
+ error_msg_and_die("short read");
+ }
+}
+
+ssize_t safe_read(int fd, void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = read(fd, buf, count);
+ } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+ssize_t safe_write(int fd, const void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = write(fd, buf, count);
+ } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+ssize_t full_read(int fd, void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len)
+ {
+ cc = safe_read(fd, buf, len);
+
+ if (cc < 0)
+ {
+ if (total)
+ {
+ /* we already have some! */
+ /* user can do another read to know the error code */
+ return total;
+ }
+ return cc; /* read() returns -1 on failure. */
+ }
+ if (cc == 0)
+ break;
+ buf = ((char *)buf) + cc;
+ total += cc;
+ len -= cc;
+ }
+
+ return total;
+}
+
+ssize_t full_write(int fd, const void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len)
+ {
+ cc = safe_write(fd, buf, len);
+
+ if (cc < 0)
+ {
+ if (total)
+ {
+ /* we already wrote some! */
+ /* user can do another write to know the error code */
+ return total;
+ }
+ return cc; /* write() returns -1 on failure. */
+ }
+
+ total += cc;
+ buf = ((const char *)buf) + cc;
+ len -= cc;
+ }
+
+ return total;
+}
+
+ssize_t full_write_str(int fd, const char *buf)
+{
+ return full_write(fd, buf, strlen(buf));
+}
diff --git a/src/lib/read_write.h b/src/lib/read_write.h
new file mode 100644
index 00000000..054a1a9a
--- /dev/null
+++ b/src/lib/read_write.h
@@ -0,0 +1,49 @@
+/*
+ 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 READ_WRITE_H
+#define READ_WRITE_H
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// NB: will return short read on error, not -1,
+// if some data was read before error occurred
+void xread(int fd, void *buf, size_t count);
+
+ssize_t safe_read(int fd, void *buf, size_t count);
+ssize_t safe_write(int fd, const void *buf, size_t count);
+
+ssize_t full_read(int fd, void *buf, size_t count);
+ssize_t full_write(int fd, const void *buf, size_t count);
+
+ssize_t full_write_str(int fd, const char *buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/run_event.c b/src/lib/run_event.c
new file mode 100644
index 00000000..23517923
--- /dev/null
+++ b/src/lib/run_event.c
@@ -0,0 +1,276 @@
+/*
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 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 run_event(struct run_event_state *state,
+ const char *dump_dir_name,
+ const char *event
+) {
+ FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+ if (!conffile)
+ {
+ error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf");
+ return 1;
+ }
+ 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 = 0;
+ struct dump_dir *dd = NULL;
+ char *line;
+ while ((line = xmalloc_fgetline(conffile)) != NULL)
+ {
+ /* Line has form: [VAR=VAL]... PROG [ARGS] */
+ char *p = skip_whitespace(line);
+ if (*p == '\0' || *p == '#')
+ goto next_line; /* empty or comment line, skip */
+
+ VERB3 log("%s: line '%s'", __func__, p);
+
+ while (1) /* word loop */
+ {
+ char *end_word = skip_non_whitespace(p);
+ char *next_word = skip_whitespace(end_word);
+
+ /* *end_word = '\0'; - BUG, truncates command */
+
+ /* If there is no '=' in this word... */
+ char *line_val = strchr(p, '=');
+ if (!line_val || line_val >= end_word)
+ break; /* ...we found the start of a command */
+
+ *end_word = '\0';
+
+ /* Current word has VAR=VAL form. line_val => VAL */
+ *line_val++ = '\0';
+
+ const char *real_val;
+ char *malloced_val = NULL;
+
+ /* Is it EVENT? */
+ if (strcmp(p, "EVENT") == 0)
+ real_val = event;
+ else
+ {
+ /* Get this name from dump dir */
+ if (!dd)
+ {
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ goto stop; /* error (note: dd_opendir logged error msg) */
+ }
+ real_val = malloced_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,
+ (int)(strchrnul(real_val, '\n') - real_val), real_val,
+ line_val);
+ free(malloced_val);
+ goto next_line; /* no */
+ }
+ free(malloced_val);
+
+ /* Go to next word */
+ 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, execute its command(s) in shell */
+ {
+ 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] */
+
+ /* Wait for child to actually exit, collect status */
+ int status;
+ waitpid(pid, &status, 0);
+
+ if (status != 0)
+ {
+ retval = WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ retval = WTERMSIG(status) + 128;
+ break;
+ }
+ }
+
+ if (state->post_run_callback)
+ {
+ retval = state->post_run_callback(dump_dir_name, state->post_run_param);
+ if (retval != 0)
+ break;
+ }
+
+ next_line:
+ free(line);
+ } /* end of line loop */
+
+ stop:
+ free(line);
+ dd_close(dd);
+ fclose(conffile);
+
+ return retval;
+}
+
+char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx)
+{
+ FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+ if (!conffile)
+ {
+ error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf");
+ return NULL;
+ }
+
+ /* We check "dump_dir_name == NULL" later.
+ * Prevent the possibility that both dump_dir_name
+ * and dd are non-NULL (which does not make sense)
+ */
+ if (dd)
+ dump_dir_name = NULL;
+
+ unsigned pfx_len = strlen(pfx);
+ struct strbuf *result = strbuf_new();
+ char *line;
+ while ((line = xmalloc_fgetline(conffile)) != NULL)
+ {
+ /* Line has form: [VAR=VAL]... PROG [ARGS] */
+ char *p = skip_whitespace(line);
+ if (*p == '\0' || *p == '#')
+ goto next_line; /* empty or comment line, skip */
+
+ VERB3 log("%s: line '%s'", __func__, p);
+
+ while (1) /* word loop */
+ {
+ char *end_word = skip_non_whitespace(p);
+ char *next_word = skip_whitespace(end_word);
+ *end_word = '\0';
+
+ /* If there is no '=' in this word... */
+ char *line_val = strchr(p, '=');
+ if (!line_val)
+ break; /* ...we found the start of a command */
+
+ /* Current word has VAR=VAL form. line_val => VAL */
+ *line_val++ = '\0';
+
+ /* Is it EVENT? */
+ if (strcmp(p, "EVENT") == 0)
+ {
+ if (strncmp(line_val, pfx, pfx_len) != 0)
+ goto next_line; /* prefix doesn't match */
+ /* (Ab)use line to save matching "\nEVENT_VAL\n" */
+ sprintf(line, "\n%s\n", line_val);
+ }
+ else
+ {
+ /* Get this name from dump dir */
+ if (!dd)
+ {
+ /* Without dir name to match, we assume match for this expr */
+ if (!dump_dir_name)
+ goto next_word;
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ goto stop; /* error (note: dd_opendir logged error msg) */
+ }
+ char *real_val = dd_load_text(dd, p);
+ /* Does VAL match? */
+ if (strcmp(real_val, line_val) != 0)
+ {
+ VERB3 log("var '%s': '%s'!='%s', skipping line", p, real_val, line_val);
+ free(real_val);
+ goto next_line; /* no */
+ }
+ free(real_val);
+ }
+
+ next_word:
+ /* Go to next word */
+ p = next_word;
+ } /* end of word loop */
+
+ if (line[0] == '\n' /* do we *have* saved matched "\nEVENT_VAL\n"? */
+ /* and does result->buf NOT yet have VAL? */
+ && strncmp(result->buf, line + 1, strlen(line + 1)) != 0
+ && !strstr(result->buf, line)
+ ) {
+ /* Add "EVENT_VAL\n" */
+ strbuf_append_str(result, line + 1);
+ }
+
+ next_line:
+ free(line);
+ } /* end of line loop */
+
+ stop:
+ free(line);
+ if (dump_dir_name != NULL)
+ dd_close(dd);
+ fclose(conffile);
+
+ return strbuf_free_nobuf(result);
+}
diff --git a/src/lib/skip_whitespace.c b/src/lib/skip_whitespace.c
new file mode 100644
index 00000000..816928bf
--- /dev/null
+++ b/src/lib/skip_whitespace.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "abrtlib.h"
+
+char* skip_whitespace(const char *s)
+{
+ /* NB: isspace('\0') returns 0 */
+ while (isspace(*s)) ++s;
+
+ return (char *) s;
+}
+
+char* skip_non_whitespace(const char *s)
+{
+ while (*s && !isspace(*s)) ++s;
+
+ return (char *) s;
+}
diff --git a/src/lib/spawn.c b/src/lib/spawn.c
new file mode 100644
index 00000000..068f4ac7
--- /dev/null
+++ b/src/lib/spawn.c
@@ -0,0 +1,156 @@
+/*
+ * Utility routines.
+ *
+ * Licensed under GPLv2, see file COPYING in this tarball for details.
+ */
+#include "abrtlib.h"
+
+static char *concat_str_vector(char **strings)
+{
+ if (!strings[0])
+ return xzalloc(1); // returns ""
+
+ unsigned len = 0;
+ char **spp = strings;
+ while (*spp)
+ len += strlen(*spp++) + 1;
+
+ char *result = xmalloc(len);
+
+ char *r = result;
+ spp = strings;
+ while (*spp) {
+ r = stpcpy(r, *spp++);
+ *r++ = ' ';
+ }
+ *--r = '\0';
+
+ return result;
+}
+
+/* Returns pid */
+pid_t fork_execv_on_steroids(int flags,
+ char **argv,
+ int *pipefds,
+ char **unsetenv_vec,
+ const char *dir,
+ uid_t uid)
+{
+ pid_t child;
+ /* Reminder: [0] is read end, [1] is write end */
+ int pipe_to_child[2];
+ int pipe_fm_child[2];
+
+ /* Sanitize flags */
+ if (!pipefds)
+ flags &= ~(EXECFLG_INPUT | EXECFLG_OUTPUT);
+
+ if (flags & EXECFLG_INPUT)
+ xpipe(pipe_to_child);
+ if (flags & EXECFLG_OUTPUT)
+ xpipe(pipe_fm_child);
+
+ fflush(NULL);
+ child = fork();
+ if (child == -1) {
+ perror_msg_and_die("fork");
+ }
+ if (child == 0) {
+ /* Child */
+
+ if (dir)
+ xchdir(dir);
+
+ if (flags & EXECFLG_SETGUID) {
+ struct passwd* pw = getpwuid(uid);
+ gid_t gid = pw ? pw->pw_gid : uid;
+ setgroups(1, &gid);
+ xsetregid(gid, gid);
+ xsetreuid(uid, uid);
+ }
+
+ if (unsetenv_vec) {
+ while (*unsetenv_vec)
+ unsetenv(*unsetenv_vec++);
+ }
+
+ /* Play with stdio descriptors */
+ if (flags & EXECFLG_INPUT) {
+ xmove_fd(pipe_to_child[0], STDIN_FILENO);
+ close(pipe_to_child[1]);
+ } else if (flags & EXECFLG_INPUT_NUL) {
+ xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
+ }
+ if (flags & EXECFLG_OUTPUT) {
+ xmove_fd(pipe_fm_child[1], STDOUT_FILENO);
+ close(pipe_fm_child[0]);
+ } else if (flags & EXECFLG_OUTPUT_NUL) {
+ xmove_fd(xopen("/dev/null", O_RDWR), STDOUT_FILENO);
+ }
+
+ /* This should be done BEFORE stderr redirect */
+ VERB1 {
+ char *r = concat_str_vector(argv);
+ log("Executing: %s", r);
+ free(r);
+ }
+
+ if (flags & EXECFLG_ERR2OUT) {
+ /* Want parent to see errors in the same stream */
+ xdup2(STDOUT_FILENO, STDERR_FILENO);
+ } else if (flags & EXECFLG_ERR_NUL) {
+ xmove_fd(xopen("/dev/null", O_RDWR), STDERR_FILENO);
+ }
+
+ if (flags & EXECFLG_SETSID)
+ setsid();
+
+ 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 */
+ }
+
+ if (flags & EXECFLG_INPUT) {
+ close(pipe_to_child[0]);
+ pipefds[1] = pipe_to_child[1];
+ }
+ if (flags & EXECFLG_OUTPUT) {
+ close(pipe_fm_child[1]);
+ pipefds[0] = pipe_fm_child[0];
+ }
+
+ return child;
+}
+
+char *run_in_shell_and_save_output(int flags,
+ const char *cmd,
+ const char *dir,
+ size_t *size_p)
+{
+ flags |= EXECFLG_OUTPUT;
+ flags &= ~EXECFLG_INPUT;
+
+ const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
+ int pipeout[2];
+ pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout,
+ /*unsetenv_vec:*/ NULL, dir, /*uid (unused):*/ 0);
+
+ size_t pos = 0;
+ char *result = NULL;
+ while (1) {
+ result = (char*) xrealloc(result, pos + 4*1024 + 1);
+ size_t sz = safe_read(pipeout[0], result + pos, 4*1024);
+ if (sz <= 0) {
+ break;
+ }
+ pos += sz;
+ }
+ result[pos] = '\0';
+ if (size_p)
+ *size_p = pos;
+ close(pipeout[0]);
+ waitpid(child, NULL, 0);
+
+ return result;
+}
diff --git a/src/lib/stdio_helpers.c b/src/lib/stdio_helpers.c
new file mode 100644
index 00000000..81cf5d75
--- /dev/null
+++ b/src/lib/stdio_helpers.c
@@ -0,0 +1,69 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2001 Matt Krai
+ * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005, 2006 Rob Landley <rob@landley.net>
+ * Copyright (C) 2010 ABRT Team
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "abrtlib.h"
+
+//TODO: add sanitizing upper limit (e.g 64K, 1M, or configurable).
+//This is why we don't use GNU's getline: it doesn't have
+//any upper sanity bound on line size.
+
+static char *xmalloc_fgets_internal(FILE *file, int *sizep)
+{
+ unsigned idx = 0;
+ char *linebuf = NULL;
+
+ while (1) {
+ char *r;
+
+ linebuf = xrealloc(linebuf, idx + 0x100);
+ r = fgets(&linebuf[idx], 0x100, file);
+ if (!r) {
+ /* need to terminate the line */
+ linebuf[idx] = '\0';
+ break;
+ }
+
+ /* stupid. fgets knows the len, it should report it somehow */
+ unsigned len = strlen(&linebuf[idx]);
+
+ idx += len;
+ if (len < 0xff || linebuf[idx - 1] == '\n')
+ break; /* we found \n or EOF */
+ }
+
+ *sizep = idx;
+
+ if (!idx) {
+ /* The very first fgets returned NULL. It's EOF (or error) */
+ free(linebuf);
+ linebuf = NULL;
+ }
+ return linebuf;
+}
+
+char *xmalloc_fgets(FILE *file)
+{
+ int sz;
+ char *r = xmalloc_fgets_internal(file, &sz);
+ if (!r)
+ return r;
+ return xrealloc(r, sz + 1);
+}
+
+char *xmalloc_fgetline(FILE *file)
+{
+ int sz;
+ char *r = xmalloc_fgets_internal(file, &sz);
+ if (!r)
+ return r;
+ if (r[sz - 1] == '\n')
+ r[--sz] = '\0';
+ return xrealloc(r, sz + 1);
+}
diff --git a/src/lib/strbuf.c b/src/lib/strbuf.c
new file mode 100644
index 00000000..04a35998
--- /dev/null
+++ b/src/lib/strbuf.c
@@ -0,0 +1,147 @@
+/*
+ String buffer implementation
+
+ Copyright (C) 2009 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 prefixcmp(const char *str, const char *prefix)
+{
+ for (; ; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+int suffixcmp(const char *str, const char *suffix)
+{
+ int len_minus_suflen = strlen(str) - strlen(suffix);
+ if (len_minus_suflen < 0)
+ return len_minus_suflen;
+ else
+ return strcmp(str + len_minus_suflen, suffix);
+}
+
+struct strbuf *strbuf_new()
+{
+ struct strbuf *buf = xzalloc(sizeof(*buf));
+ /*buf->len = 0; - done by xzalloc */
+ buf->alloc = 8;
+ buf->buf = xzalloc(8);
+ return buf;
+}
+
+void strbuf_free(struct strbuf *strbuf)
+{
+ if (!strbuf)
+ return;
+ free(strbuf->buf);
+ free(strbuf);
+}
+
+char *strbuf_free_nobuf(struct strbuf *strbuf)
+{
+ char *ret = strbuf->buf;
+ free(strbuf);
+ return ret;
+}
+
+
+void strbuf_clear(struct strbuf *strbuf)
+{
+ assert(strbuf->alloc > 0);
+ strbuf->len = 0;
+ strbuf->buf[0] = '\0';
+}
+
+/* Ensures that the buffer can be extended by N+1 characters
+ * without touching malloc/realloc.
+ * Returns pointer where appended chars can be stored by the caller;
+ * increments ->len by N (therefore callers don't need to do it).
+ */
+static char *strbuf_grow(struct strbuf *strbuf, unsigned increment)
+{
+ unsigned len = strbuf->len;
+ unsigned need = strbuf->len = len + increment;
+ unsigned cur_size = strbuf->alloc;
+ if (cur_size <= need)
+ {
+ while (cur_size <= need)
+ cur_size += 64 + cur_size / 8;
+ strbuf->alloc = cur_size;
+ strbuf->buf = xrealloc(strbuf->buf, cur_size);
+ }
+ char *p = strbuf->buf + len;
+ return p;
+}
+
+struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c)
+{
+ char *p = strbuf_grow(strbuf, 1);
+ *p++ = c;
+ *p = '\0';
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str)
+{
+ unsigned len = strlen(str);
+ char *p = strbuf_grow(strbuf, len);
+ assert(strbuf->len < strbuf->alloc);
+ strcpy(p, str);
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str)
+{
+ unsigned cur_len = strbuf->len;
+ unsigned inc_len = strlen(str);
+ strbuf_grow(strbuf, inc_len);
+ assert(strbuf->len < strbuf->alloc);
+ memmove(strbuf->buf + inc_len, strbuf->buf, cur_len);
+ memcpy(strbuf->buf, str, inc_len);
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ strbuf_append_str(strbuf, string_ptr);
+ free(string_ptr);
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ strbuf_prepend_str(strbuf, string_ptr);
+ free(string_ptr);
+ return strbuf;
+}
diff --git a/src/lib/strbuf.h b/src/lib/strbuf.h
new file mode 100644
index 00000000..dc45a199
--- /dev/null
+++ b/src/lib/strbuf.h
@@ -0,0 +1,103 @@
+/*
+ strbuf.h - string buffer
+
+ Copyright (C) 2009 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 STRBUF_H
+#define STRBUF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct strbuf
+{
+ /* Size of the allocated buffer. Always > 0. */
+ int alloc;
+ /* Length of the string, without the ending \0. */
+ int len;
+ char *buf;
+};
+
+/**
+ * Creates and initializes a new string buffer.
+ * @returns
+ * It never returns NULL. The returned pointer must be released by
+ * calling the function strbuf_free().
+ */
+struct strbuf *strbuf_new();
+
+/**
+ * Releases the memory held by the string buffer.
+ * @param strbuf
+ * If the strbuf is NULL, no operation is performed.
+ */
+void strbuf_free(struct strbuf *strbuf);
+
+/**
+ * Releases the strbuf, but not the internal buffer. The internal
+ * string buffer is returned. Caller is responsible to release the
+ * returned memory using free().
+ */
+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.
+ */
+void strbuf_clear(struct strbuf *strbuf);
+
+/**
+ * The current content of the string buffer is extended by adding a
+ * character c at its end.
+ */
+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.
+ */
+struct strbuf *strbuf_append_str(struct strbuf *strbuf,
+ const char *str);
+
+/**
+ * The current content of the string buffer is extended by inserting a
+ * string str at its beginning.
+ */
+struct strbuf *strbuf_prepend_str(struct strbuf *strbuf,
+ const char *str);
+
+/**
+ * The current content of the string buffer is extended by adding a
+ * sequence of data formatted as the format argument specifies.
+ */
+struct strbuf *strbuf_append_strf(struct strbuf *strbuf,
+ const char *format, ...);
+
+/**
+ * The current content of the string buffer is extended by inserting a
+ * sequence of data formatted as the format argument specifies at the
+ * buffer beginning.
+ */
+struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf,
+ const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/stringops.cpp b/src/lib/stringops.cpp
new file mode 100644
index 00000000..7bc5413f
--- /dev/null
+++ b/src/lib/stringops.cpp
@@ -0,0 +1,57 @@
+/*
+ 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/test.cpp b/src/lib/test.cpp
new file mode 100644
index 00000000..a548dc21
--- /dev/null
+++ b/src/lib/test.cpp
@@ -0,0 +1,105 @@
+/*
+ test.cpp - simple library test
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 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 "MiddleWare.h"
+#include "DebugDump.h"
+#include "CrashTypes.h"
+#include <iostream>
+#include <sys/types.h>
+#include <unistd.h>
+#include <iostream>
+
+
+int main(int argc, char** argv)
+{
+
+ if (argc < 2)
+ {
+ std::cerr << "Usage: " << argv[0] << " <DebugDumpDir>" << std::endl;
+ return -1;
+ }
+ try
+ {
+ CMiddleWare middleWare(PLUGINS_CONF_DIR,
+ PLUGINS_LIB_DIR);
+ vector_map_string_t loaded_plugins;
+ middleWare.RegisterPlugin("CCpp");
+ middleWare.RegisterPlugin("Mailx");
+ middleWare.RegisterPlugin("Logger");
+ middleWare.RegisterPlugin("SQLite3");
+ middleWare.SetDatabase("SQLite3");
+ middleWare.SetOpenGPGCheck(false);
+ middleWare.AddActionOrReporter("Logger", "");
+ middleWare.AddAnalyzerActionOrReporter("CCpp", "Mailx", "");
+
+ loaded_plugins = middleWare.GetPluginsInfo();
+ std::cout << "Loaded plugins" << std::endl;
+ int ii;
+ for ( ii = 0; ii < loaded_plugins.size(); ii++)
+ {
+ std::cout << "-------------------------------------------" << std::endl;
+ map_plugin_settings_t settings;
+ std::cout << "Enabled: " << loaded_plugins[ii]["Enabled"] << std::endl;
+ std::cout << "Type: " << loaded_plugins[ii]["Type"] << std::endl;
+ std::cout << "Name: " << loaded_plugins[ii]["Name"] << std::endl;
+ std::cout << "Version: " << loaded_plugins[ii]["Version"] << std::endl;
+ std::cout << "Description: " << loaded_plugins[ii]["Description"] << std::endl;
+ std::cout << "Email: " << loaded_plugins[ii]["Email"] << std::endl;
+ std::cout << "WWW: " << loaded_plugins[ii]["WWW"] << std::endl;
+ std::cout << "GTKBuilder: " << loaded_plugins[ii]["GTKBuilder"] << std::endl;
+ if (loaded_plugins[ii]["Enabled"] == "yes")
+ {
+ std::cout << std::endl << "Settings: " << std::endl;
+ settings = middleWare.GetPluginSettings(loaded_plugins[ii]["Name"]);
+ map_plugin_settings_t::iterator it;
+ for (it = settings.begin(); it != settings.end(); it++)
+ {
+ std::cout << "\t" << it->first << ": " << it->second << std::endl;
+ }
+ }
+ std::cout << "-------------------------------------------" << std::endl;
+ }
+ /* Try to save it into DB */
+ map_crash_data_t crashInfo;
+ if (middleWare.SaveDebugDump(argv[1], crashInfo))
+ {
+ std::cout << "Application Crashed! " <<
+ crashInfo[FILENAME_PACKAGE][CD_CONTENT] << ", " <<
+ crashInfo[FILENAME_EXECUTABLE][CD_CONTENT] << ", " <<
+ crashInfo[CD_COUNT][CD_CONTENT] << ", " << std::endl;
+
+ /* Get Report, so user can change data (remove private stuff)
+ * If we do not want user interaction, just send data immediately
+ */
+ map_crash_data_t crashReport;
+ middleWare.CreateCrashReport(crashInfo[FILENAME_DUPHASH][CD_CONTENT],
+ crashInfo[CD_UID][CD_CONTENT],
+ crashReport);
+ /* Report crash */
+ middleWare.Report(crashReport);
+ }
+ }
+ catch (std::string sError)
+ {
+ std::cerr << sError << std::endl;
+ }
+
+ return 0;
+}
diff --git a/src/lib/time.c b/src/lib/time.c
new file mode 100644
index 00000000..37ade2cc
--- /dev/null
+++ b/src/lib/time.c
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+#include "abrtlib.h"
+
+#define ENABLE_MONOTONIC_SYSCALL 1
+
+#if ENABLE_MONOTONIC_SYSCALL
+
+#include <sys/syscall.h>
+/* Old glibc (< 2.3.4) does not provide this constant. We use syscall
+ * directly so this definition is safe. */
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC 1
+#endif
+
+/* libc has incredibly messy way of doing this,
+ * typically requiring -lrt. We just skip all this mess */
+static void get_mono(struct timespec *ts)
+{
+ if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts))
+ error_msg_and_die("clock_gettime(MONOTONIC) failed");
+}
+unsigned long long monotonic_ns(void)
+{
+ struct timespec ts;
+ get_mono(&ts);
+ return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+unsigned long long monotonic_us(void)
+{
+ struct timespec ts;
+ get_mono(&ts);
+ return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
+}
+unsigned monotonic_sec(void)
+{
+ struct timespec ts;
+ get_mono(&ts);
+ return ts.tv_sec;
+}
+
+#else
+
+unsigned long long monotonic_ns(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
+}
+unsigned long long monotonic_us(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000ULL + tv.tv_usec;
+}
+unsigned monotonic_sec(void)
+{
+ return time(NULL);
+}
+
+#endif
diff --git a/src/lib/xatonum.c b/src/lib/xatonum.c
new file mode 100644
index 00000000..3b22071f
--- /dev/null
+++ b/src/lib/xatonum.c
@@ -0,0 +1,50 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "abrtlib.h"
+
+unsigned xatou(const char *numstr)
+{
+ unsigned long r;
+ int old_errno;
+ char *e;
+
+ if (*numstr < '0' || *numstr > '9')
+ goto inval;
+
+ old_errno = errno;
+ errno = 0;
+ r = strtoul(numstr, &e, 10);
+ if (errno || numstr == e || *e != '\0' || r > UINT_MAX)
+ goto inval; /* error / no digits / illegal trailing chars */
+ errno = old_errno; /* Ok. So restore errno. */
+ return r;
+
+inval:
+ error_msg_and_die("invalid number '%s'", numstr);
+}
+
+int xatoi_u(const char *numstr)
+{
+ unsigned r = xatou(numstr);
+ if (r > (unsigned)INT_MAX)
+ error_msg_and_die("invalid number '%s'", numstr);
+ return r;
+}
+
+int xatoi(const char *numstr)
+{
+ unsigned r;
+
+ if (*numstr != '-')
+ return xatoi_u(numstr);
+
+ r = xatou(numstr + 1);
+ if (r > (unsigned)INT_MAX + 1)
+ error_msg_and_die("invalid number '%s'", numstr);
+ return - (int)r;
+}
diff --git a/src/lib/xconnect.c b/src/lib/xconnect.c
new file mode 100644
index 00000000..0d02b1a4
--- /dev/null
+++ b/src/lib/xconnect.c
@@ -0,0 +1,415 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Connect to host at port using address resolution from getaddrinfo
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "abrtlib.h"
+#include <sys/socket.h> /* netinet/in.h needs it */
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#define ENABLE_FEATURE_IPV6 1
+#define ENABLE_FEATURE_PREFER_IPV4_ADDRESS 1
+
+static const int const_int_1 = 1;
+
+void setsockopt_reuseaddr(int fd)
+{
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1));
+}
+int setsockopt_broadcast(int fd)
+{
+ return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+}
+int setsockopt_bindtodevice(int fd, const char *iface)
+{
+ int r;
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+ /* NB: passing (iface, strlen(iface) + 1) does not work!
+ * (maybe it works on _some_ kernels, but not on 2.6.26)
+ * Actually, ifr_name is at offset 0, and in practice
+ * just giving char[IFNAMSIZ] instead of struct ifreq works too.
+ * But just in case it's not true on some obscure arch... */
+ r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+ if (r)
+ perror_msg("can't bind to interface %s", iface);
+ return r;
+}
+
+len_and_sockaddr* get_sock_lsa(int fd)
+{
+ len_and_sockaddr lsa;
+ len_and_sockaddr *lsa_ptr;
+
+ lsa.len = LSA_SIZEOF_SA;
+ if (getsockname(fd, &lsa.u.sa, &lsa.len) != 0)
+ return NULL;
+
+ lsa_ptr = (len_and_sockaddr *)xzalloc(LSA_LEN_SIZE + lsa.len);
+ if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */
+ lsa_ptr->len = lsa.len;
+ getsockname(fd, &lsa_ptr->u.sa, &lsa_ptr->len);
+ } else {
+ memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len);
+ }
+ return lsa_ptr;
+}
+
+void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
+{
+ if (connect(s, s_addr, addrlen) < 0) {
+ close(s);
+ if (s_addr->sa_family == AF_INET)
+ perror_msg_and_die("%s (%s)",
+ "cannot connect to remote host",
+ inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
+ perror_msg_and_die("cannot connect to remote host");
+ }
+}
+
+/* Return port number for a service.
+ * If "port" is a number use it as the port.
+ * If "port" is a name it is looked up in /etc/services,
+ * if it isnt found return default_port
+ */
+unsigned lookup_port(const char *port, const char *protocol, unsigned default_port)
+{
+ unsigned port_nr = default_port;
+ if (port) {
+ int old_errno;
+ char *end;
+
+ /* Since this is a lib function, we're not allowed to reset errno to 0.
+ * Doing so could break an app that is deferring checking of errno. */
+ old_errno = errno;
+ errno = 0;
+ port_nr = strtoul(port, &end, 10);
+ if (errno || *end || port_nr > 65535) {
+ struct servent *tserv = getservbyname(port, protocol);
+ port_nr = default_port;
+ if (tserv)
+ port_nr = ntohs(tserv->s_port);
+ }
+ errno = old_errno;
+ }
+ return (uint16_t)port_nr;
+}
+
+int get_nport(const struct sockaddr *sa)
+{
+#if ENABLE_FEATURE_IPV6
+ if (sa->sa_family == AF_INET6) {
+ return ((struct sockaddr_in6*)sa)->sin6_port;
+ }
+#endif
+ if (sa->sa_family == AF_INET) {
+ return ((struct sockaddr_in*)sa)->sin_port;
+ }
+ /* What? UNIX socket? IPX?? :) */
+ return -1;
+}
+
+void set_nport(len_and_sockaddr *lsa, unsigned port)
+{
+#if ENABLE_FEATURE_IPV6
+ if (lsa->u.sa.sa_family == AF_INET6) {
+ lsa->u.sin6.sin6_port = port;
+ return;
+ }
+#endif
+ if (lsa->u.sa.sa_family == AF_INET) {
+ lsa->u.sin.sin_port = port;
+ return;
+ }
+ /* What? UNIX socket? IPX?? :) */
+}
+
+/* We hijack this constant to mean something else */
+/* It doesn't hurt because we will remove this bit anyway */
+#define DIE_ON_ERROR AI_CANONNAME
+
+/* host: "1.2.3.4[:port]", "www.google.com[:port]"
+ * port: if neither of above specifies port # */
+static len_and_sockaddr* str2sockaddr(
+ const char *host, int port,
+ sa_family_t af,
+ int ai_flags)
+{
+ int rc;
+ len_and_sockaddr *r;
+ struct addrinfo *result = NULL;
+ struct addrinfo *used_res;
+ const char *org_host = host; /* only for error msg */
+ const char *cp;
+ struct addrinfo hint;
+
+ r = NULL;
+
+ /* Ugly parsing of host:addr */
+ if (ENABLE_FEATURE_IPV6 && host[0] == '[') {
+ /* Even uglier parsing of [xx]:nn */
+ host++;
+ cp = strchr(host, ']');
+ if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
+ /* Malformed: must be [xx]:nn or [xx] */
+ error_msg("bad address '%s'", org_host);
+ if (ai_flags & DIE_ON_ERROR)
+ xfunc_die();
+ return NULL;
+ }
+ } else {
+ cp = strrchr(host, ':');
+ if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) {
+ /* There is more than one ':' (e.g. "::1") */
+ cp = NULL; /* it's not a port spec */
+ }
+ }
+ if (cp) { /* points to ":" or "]:" */
+ int sz = cp - host + 1;
+ char *hbuf = (char*)alloca(sz);
+ hbuf[--sz] = '\0';
+ host = strncpy(hbuf, host, sz);
+ if (ENABLE_FEATURE_IPV6 && *cp != ':') {
+ cp++; /* skip ']' */
+ if (*cp == '\0') /* [xx] without port */
+ goto skip;
+ }
+ cp++; /* skip ':' */
+ char *end;
+ errno = 0;
+ port = strtoul(cp, &end, 10);
+ if (errno || *end || (unsigned)port > 0xffff) {
+ error_msg("bad port spec '%s'", org_host);
+ if (ai_flags & DIE_ON_ERROR)
+ xfunc_die();
+ return NULL;
+ }
+ skip: ;
+ }
+
+ memset(&hint, 0 , sizeof(hint));
+#if !ENABLE_FEATURE_IPV6
+ hint.ai_family = AF_INET; /* do not try to find IPv6 */
+#else
+ hint.ai_family = af;
+#endif
+ /* Needed. Or else we will get each address thrice (or more)
+ * for each possible socket type (tcp,udp,raw...): */
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_flags = ai_flags & ~DIE_ON_ERROR;
+ rc = getaddrinfo(host, NULL, &hint, &result);
+ if (rc || !result) {
+ error_msg("bad address '%s'", org_host);
+ if (ai_flags & DIE_ON_ERROR)
+ xfunc_die();
+ goto ret;
+ }
+ used_res = result;
+#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS
+ while (1) {
+ if (used_res->ai_family == AF_INET)
+ break;
+ used_res = used_res->ai_next;
+ if (!used_res) {
+ used_res = result;
+ break;
+ }
+ }
+#endif
+ r = (len_and_sockaddr *)xmalloc(offsetof(len_and_sockaddr, u.sa) + used_res->ai_addrlen);
+ r->len = used_res->ai_addrlen;
+ memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen);
+ set_nport(r, htons(port));
+ ret:
+ freeaddrinfo(result);
+ return r;
+}
+#if !ENABLE_FEATURE_IPV6
+#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags)
+#endif
+
+#if ENABLE_FEATURE_IPV6
+len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af)
+{
+ return str2sockaddr(host, port, af, 0);
+}
+
+len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af)
+{
+ return str2sockaddr(host, port, af, DIE_ON_ERROR);
+}
+#endif
+
+len_and_sockaddr* host2sockaddr(const char *host, int port)
+{
+ return str2sockaddr(host, port, AF_UNSPEC, 0);
+}
+
+len_and_sockaddr* xhost2sockaddr(const char *host, int port)
+{
+ return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR);
+}
+
+len_and_sockaddr* xdotted2sockaddr(const char *host, int port)
+{
+ return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR);
+}
+
+#undef xsocket_type
+int xsocket_type(len_and_sockaddr **lsap, int family, int sock_type)
+{
+ len_and_sockaddr *lsa;
+ int fd;
+ int len;
+
+#if ENABLE_FEATURE_IPV6
+ if (family == AF_UNSPEC) {
+ fd = socket(AF_INET6, sock_type, 0);
+ if (fd >= 0) {
+ family = AF_INET6;
+ goto done;
+ }
+ family = AF_INET;
+ }
+#endif
+ fd = xsocket(family, sock_type, 0);
+ len = sizeof(struct sockaddr_in);
+#if ENABLE_FEATURE_IPV6
+ if (family == AF_INET6) {
+ done:
+ len = sizeof(struct sockaddr_in6);
+ }
+#endif
+ lsa = (len_and_sockaddr *)xzalloc(offsetof(len_and_sockaddr, u.sa) + len);
+ lsa->len = len;
+ lsa->u.sa.sa_family = family;
+ *lsap = lsa;
+ return fd;
+}
+
+int xsocket_stream(len_and_sockaddr **lsap)
+{
+ return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM);
+}
+
+static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
+{
+ int fd;
+ len_and_sockaddr *lsa;
+
+ if (bindaddr && bindaddr[0]) {
+ lsa = xdotted2sockaddr(bindaddr, port);
+ /* user specified bind addr dictates family */
+ fd = xsocket(lsa->u.sa.sa_family, sock_type, 0);
+ } else {
+ fd = xsocket_type(&lsa, AF_UNSPEC, sock_type);
+ set_nport(lsa, htons(port));
+ }
+ setsockopt_reuseaddr(fd);
+ xbind(fd, &lsa->u.sa, lsa->len);
+ free(lsa);
+ return fd;
+}
+
+int create_and_bind_stream_or_die(const char *bindaddr, int port)
+{
+ return create_and_bind_or_die(bindaddr, port, SOCK_STREAM);
+}
+
+int create_and_bind_dgram_or_die(const char *bindaddr, int port)
+{
+ return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM);
+}
+
+
+int create_and_connect_stream_or_die(const char *peer, int port)
+{
+ int fd;
+ len_and_sockaddr *lsa;
+
+ lsa = xhost2sockaddr(peer, port);
+ fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
+ setsockopt_reuseaddr(fd);
+ xconnect(fd, &lsa->u.sa, lsa->len);
+ free(lsa);
+ return fd;
+}
+
+int xconnect_stream(const len_and_sockaddr *lsa)
+{
+ int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
+ xconnect(fd, &lsa->u.sa, lsa->len);
+ return fd;
+}
+
+/* We hijack this constant to mean something else */
+/* It doesn't hurt because we will add this bit anyway */
+#define IGNORE_PORT NI_NUMERICSERV
+static char* sockaddr2str(const struct sockaddr *sa, int flags)
+{
+ char host[128];
+ char serv[16];
+ int rc;
+ socklen_t salen;
+
+ salen = LSA_SIZEOF_SA;
+#if ENABLE_FEATURE_IPV6
+ if (sa->sa_family == AF_INET)
+ salen = sizeof(struct sockaddr_in);
+ if (sa->sa_family == AF_INET6)
+ salen = sizeof(struct sockaddr_in6);
+#endif
+ rc = getnameinfo(sa, salen,
+ host, sizeof(host),
+ /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */
+ serv, sizeof(serv),
+ /* do not resolve port# into service _name_ */
+ flags | NI_NUMERICSERV
+ );
+ if (rc)
+ return NULL;
+ if (flags & IGNORE_PORT)
+ return xstrdup(host);
+#if ENABLE_FEATURE_IPV6
+ if (sa->sa_family == AF_INET6) {
+ if (strchr(host, ':')) /* heh, it's not a resolved hostname */
+ return xasprintf("[%s]:%s", host, serv);
+ /*return xasprintf("%s:%s", host, serv);*/
+ /* - fall through instead */
+ }
+#endif
+ /* For now we don't support anything else, so it has to be INET */
+ /*if (sa->sa_family == AF_INET)*/
+ return xasprintf("%s:%s", host, serv);
+ /*return xstrdup(host);*/
+}
+
+char* xmalloc_sockaddr2host(const struct sockaddr *sa)
+{
+ return sockaddr2str(sa, 0);
+}
+
+char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa)
+{
+ return sockaddr2str(sa, IGNORE_PORT);
+}
+
+char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
+{
+ return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
+}
+char* xmalloc_sockaddr2dotted(const struct sockaddr *sa)
+{
+ return sockaddr2str(sa, NI_NUMERICHOST);
+}
+
+char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
+{
+ return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT);
+}
diff --git a/src/lib/xfuncs.c b/src/lib/xfuncs.c
new file mode 100644
index 00000000..d5166037
--- /dev/null
+++ b/src/lib/xfuncs.c
@@ -0,0 +1,400 @@
+/*
+ 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.
+*/
+
+/*
+ * Utility routines.
+ *
+ */
+#include "abrtlib.h"
+
+/* 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 ndelay_off(int fd)
+{
+ return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
+}
+
+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)
+{
+ void *ptr = malloc(size);
+ if (ptr == NULL && size != 0)
+ die_out_of_memory();
+ return ptr;
+}
+
+// Die if we can't resize previously allocated memory. (This returns a pointer
+// to the new memory, which may or may not be the same as the old memory.
+// It'll copy the contents to a new chunk and free the old one if necessary.)
+void* xrealloc(void *ptr, size_t size)
+{
+ ptr = realloc(ptr, size);
+ if (ptr == NULL && size != 0)
+ die_out_of_memory();
+ return ptr;
+}
+
+// Die if we can't allocate and zero size bytes of memory.
+void* xzalloc(size_t size)
+{
+ void *ptr = xmalloc(size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+// Die if we can't copy a string to freshly allocated memory.
+char* xstrdup(const char *s)
+{
+ char *t;
+ if (s == NULL)
+ return NULL;
+
+ t = strdup(s);
+
+ if (t == NULL)
+ die_out_of_memory();
+
+ return t;
+}
+
+// Die if we can't allocate n+1 bytes (space for the null terminator) and copy
+// the (possibly truncated to length n) string into it.
+char* xstrndup(const char *s, int n)
+{
+ int m;
+ char *t;
+
+ /* We can just xmalloc(n+1) and strncpy into it, */
+ /* but think about xstrndup("abc", 10000) wastage! */
+ m = n;
+ t = (char*) s;
+ while (m)
+ {
+ if (!*t) break;
+ m--;
+ t++;
+ }
+ n -= m;
+ t = (char*) xmalloc(n + 1);
+ t[n] = '\0';
+
+ return (char*) memcpy(t, s, n);
+}
+
+void xpipe(int filedes[2])
+{
+ if (pipe(filedes))
+ perror_msg_and_die("can't create pipe");
+}
+
+void xdup(int from)
+{
+ if (dup(from) < 0)
+ perror_msg_and_die("can't duplicate file descriptor");
+}
+
+void xdup2(int from, int to)
+{
+ if (dup2(from, to) != to)
+ perror_msg_and_die("can't duplicate file descriptor");
+}
+
+// "Renumber" opened fd
+void xmove_fd(int from, int to)
+{
+ if (from == to)
+ return;
+ xdup2(from, to);
+ close(from);
+}
+
+// Die with an error message if we can't write the entire buffer.
+void xwrite(int fd, const void *buf, size_t count)
+{
+ if (count == 0)
+ return;
+ ssize_t size = full_write(fd, buf, count);
+ if ((size_t)size != count)
+ error_msg_and_die("short write");
+}
+
+void xwrite_str(int fd, const char *str)
+{
+ xwrite(fd, str, strlen(str));
+}
+
+// Die with an error message if we can't lseek to the right spot.
+off_t xlseek(int fd, off_t offset, int whence)
+{
+ off_t off = lseek(fd, offset, whence);
+ if (off == (off_t)-1) {
+ if (whence == SEEK_SET)
+ perror_msg_and_die("lseek(%llu)", (long long)offset);
+ perror_msg_and_die("lseek");
+ }
+ return off;
+}
+
+void xchdir(const char *path)
+{
+ if (chdir(path))
+ perror_msg_and_die("chdir(%s)", path);
+}
+
+char* xvasprintf(const char *format, va_list p)
+{
+ int r;
+ char *string_ptr;
+
+#if 1
+ // GNU extension
+ r = vasprintf(&string_ptr, format, p);
+#else
+ // Bloat for systems that haven't got the GNU extension.
+ va_list p2;
+ va_copy(p2, p);
+ r = vsnprintf(NULL, 0, format, p);
+ string_ptr = xmalloc(r+1);
+ r = vsnprintf(string_ptr, r+1, format, p2);
+ va_end(p2);
+#endif
+
+ if (r < 0)
+ die_out_of_memory();
+ return string_ptr;
+}
+
+// Die with an error message if we can't malloc() enough space and do an
+// sprintf() into that space.
+char* xasprintf(const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ return string_ptr;
+}
+
+void xsetenv(const char *key, const char *value)
+{
+ if (setenv(key, value, 1))
+ die_out_of_memory();
+}
+
+// Die with an error message if we can't open a new socket.
+int xsocket(int domain, int type, int protocol)
+{
+ int r = socket(domain, type, protocol);
+ if (r < 0)
+ {
+ const char *s = "INET";
+ if (domain == AF_PACKET) s = "PACKET";
+ if (domain == AF_NETLINK) s = "NETLINK";
+ if (domain == AF_INET6) s = "INET6";
+ perror_msg_and_die("socket(AF_%s)", s);
+ }
+
+ return r;
+}
+
+// Die with an error message if we can't bind a socket to an address.
+void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
+{
+ if (bind(sockfd, my_addr, addrlen))
+ perror_msg_and_die("bind");
+}
+
+// Die with an error message if we can't listen for connections on a socket.
+void xlisten(int s, int backlog)
+{
+ if (listen(s, backlog))
+ perror_msg_and_die("listen");
+}
+
+// Die with an error message if sendto failed.
+// Return bytes sent otherwise
+ssize_t xsendto(int s, const void *buf, size_t len,
+ const struct sockaddr *to,
+ socklen_t tolen)
+{
+ ssize_t ret = sendto(s, buf, len, 0, to, tolen);
+ if (ret < 0)
+ {
+ close(s);
+ perror_msg_and_die("sendto");
+ }
+ return ret;
+}
+
+// xstat() - a stat() which dies on failure with meaningful error message
+void xstat(const char *name, struct stat *stat_buf)
+{
+ if (stat(name, stat_buf))
+ perror_msg_and_die("can't stat '%s'", name);
+}
+
+// Die if we can't open a file and return a fd
+int xopen3(const char *pathname, int flags, int mode)
+{
+ int ret;
+ ret = open(pathname, flags, mode);
+ if (ret < 0)
+ perror_msg_and_die("can't open '%s'", pathname);
+ return ret;
+}
+
+// Die if we can't open an existing file and return a fd
+int xopen(const char *pathname, int flags)
+{
+ return xopen3(pathname, flags, 0666);
+}
+
+void xunlink(const char *pathname)
+{
+ if (unlink(pathname))
+ perror_msg_and_die("can't remove file '%s'", pathname);
+}
+
+#if 0 //UNUSED
+// Warn if we can't open a file and return a fd.
+int open3_or_warn(const char *pathname, int flags, int mode)
+{
+ int ret;
+ ret = open(pathname, flags, mode);
+ if (ret < 0)
+ perror_msg("can't open '%s'", pathname);
+ return ret;
+}
+
+// Warn if we can't open a file and return a fd.
+int open_or_warn(const char *pathname, int flags)
+{
+ return open3_or_warn(pathname, flags, 0666);
+}
+#endif
+
+/* Just testing dent->d_type == DT_REG is wrong: some filesystems
+ * do not report the type, they report DT_UNKNOWN for every dirent
+ * (and this is not a bug in filesystem, this is allowed by standards).
+ */
+int is_regular_file(struct dirent *dent, const char *dirname)
+{
+ if (dent->d_type == DT_REG)
+ return 1;
+ if (dent->d_type != DT_UNKNOWN)
+ return 0;
+
+ char *fullname = xasprintf("%s/%s", dirname, dent->d_name);
+ struct stat statbuf;
+ int r = lstat(fullname, &statbuf);
+ free(fullname);
+
+ return r == 0 && S_ISREG(statbuf.st_mode);
+}
+
+/* Is it "." or ".."? */
+/* abrtlib candidate */
+bool dot_or_dotdot(const char *filename)
+{
+ if (filename[0] != '.') return false;
+ if (filename[1] == '\0') return true;
+ if (filename[1] != '.') return false;
+ if (filename[2] == '\0') return true;
+ return false;
+}
+
+/* Find out if the last character of a string matches the one given.
+ * Don't underrun the buffer if the string length is 0.
+ */
+char *last_char_is(const char *s, int c)
+{
+ if (s && *s)
+ {
+ s += strlen(s) - 1;
+ if ((unsigned char)*s == c)
+ return (char*)s;
+ }
+ return NULL;
+}
+
+bool string_to_bool(const char *s)
+{
+ if (s[0] == '1' && s[1] == '\0')
+ return true;
+ if (strcasecmp(s, "on") == 0)
+ return true;
+ if (strcasecmp(s, "yes") == 0)
+ return true;
+ if (strcasecmp(s, "true") == 0)
+ return true;
+ return false;
+}
+
+void xseteuid(uid_t euid)
+{
+ if (seteuid(euid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'u', (long)euid);
+}
+
+void xsetegid(gid_t egid)
+{
+ if (setegid(egid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'g', (long)egid);
+}
+
+void xsetreuid(uid_t ruid, uid_t euid)
+{
+ if (setreuid(ruid, euid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'u', (long)ruid);
+}
+
+void xsetregid(gid_t rgid, gid_t egid)
+{
+ if (setregid(rgid, egid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'g', (long)rgid);
+}
+
+const char *get_home_dir(uid_t uid)
+{
+ struct passwd* pw = getpwuid(uid);
+ // TODO: handle errno
+ return pw ? pw->pw_dir : NULL;
+}