summaryrefslogtreecommitdiffstats
path: root/lib/utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils')
-rw-r--r--lib/utils/ABRTException.cpp33
-rw-r--r--lib/utils/CommLayerInner.cpp94
-rw-r--r--lib/utils/CrashTypes.cpp132
-rw-r--r--lib/utils/DebugDump.cpp521
-rw-r--r--lib/utils/Makefile.am86
-rw-r--r--lib/utils/Plugin.cpp104
-rw-r--r--lib/utils/Polkit.cpp102
-rw-r--r--lib/utils/Polkit.h42
-rw-r--r--lib/utils/abrt_curl.cpp320
-rw-r--r--lib/utils/abrt_curl.h88
-rw-r--r--lib/utils/abrt_dbus.cpp430
-rw-r--r--lib/utils/abrt_dbus.h302
-rw-r--r--lib/utils/abrt_packages.c44
-rw-r--r--lib/utils/abrt_packages.h35
-rw-r--r--lib/utils/abrt_rh_support.cpp513
-rw-r--r--lib/utils/abrt_rh_support.h47
-rw-r--r--lib/utils/abrt_xmlrpc.cpp100
-rw-r--r--lib/utils/backtrace.c846
-rw-r--r--lib/utils/backtrace.h152
-rw-r--r--lib/utils/backtrace_parser.y684
-rw-r--r--lib/utils/copyfd.cpp171
-rw-r--r--lib/utils/daemon.cpp139
-rw-r--r--lib/utils/dirsize.cpp100
-rw-r--r--lib/utils/encbase64.cpp78
-rw-r--r--lib/utils/hooklib.cpp133
-rw-r--r--lib/utils/hooklib.h21
-rw-r--r--lib/utils/logging.c143
-rw-r--r--lib/utils/logging.h83
-rw-r--r--lib/utils/make_descr.cpp236
-rw-r--r--lib/utils/numtoa.cpp34
-rw-r--r--lib/utils/parse_release.cpp64
-rw-r--r--lib/utils/read_write.c94
-rw-r--r--lib/utils/read_write.h49
-rw-r--r--lib/utils/skip_whitespace.cpp22
-rw-r--r--lib/utils/spawn.cpp142
-rw-r--r--lib/utils/strbuf.c147
-rw-r--r--lib/utils/strbuf.h51
-rw-r--r--lib/utils/stringops.cpp57
-rw-r--r--lib/utils/test.cpp107
-rw-r--r--lib/utils/time.cpp65
-rw-r--r--lib/utils/xatonum.cpp50
-rw-r--r--lib/utils/xconnect.cpp415
-rw-r--r--lib/utils/xfuncs.c391
43 files changed, 7467 insertions, 0 deletions
diff --git a/lib/utils/ABRTException.cpp b/lib/utils/ABRTException.cpp
new file mode 100644
index 00000000..0ae5d452
--- /dev/null
+++ b/lib/utils/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/lib/utils/CommLayerInner.cpp b/lib/utils/CommLayerInner.cpp
new file mode 100644
index 00000000..3c102d6e
--- /dev/null
+++ b/lib/utils/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/lib/utils/CrashTypes.cpp b/lib/utils/CrashTypes.cpp
new file mode 100644
index 00000000..96be28c1
--- /dev/null
+++ b/lib/utils/CrashTypes.cpp
@@ -0,0 +1,132 @@
+/*
+ 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 "abrt_types.h"
+#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 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/lib/utils/DebugDump.cpp b/lib/utils/DebugDump.cpp
new file mode 100644
index 00000000..30ceacc1
--- /dev/null
+++ b/lib/utils/DebugDump.cpp
@@ -0,0 +1,521 @@
+/*
+ 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 "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+static bool isdigit_str(const char *str)
+{
+ do
+ {
+ if (*str < '0' || *str > '9') return false;
+ str++;
+ } while (*str);
+ return true;
+}
+
+static std::string RemoveBackSlashes(const char *pDir)
+{
+ unsigned len = strlen(pDir);
+ while (len != 0 && pDir[len-1] == '/')
+ len--;
+ return std::string(pDir, len);
+}
+
+static bool ExistFileDir(const char *pPath)
+{
+ struct stat buf;
+ if (stat(pPath, &buf) == 0)
+ {
+ if (S_ISDIR(buf.st_mode) || S_ISREG(buf.st_mode))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void LoadTextFile(const char *pPath, std::string& pData);
+
+CDebugDump::CDebugDump() :
+ m_sDebugDumpDir(""),
+ m_pGetNextFileDir(NULL),
+ m_bOpened(false),
+ m_bLocked(false),
+ m_uid(0),
+ m_gid(0)
+{}
+
+CDebugDump::~CDebugDump()
+{
+ /* Paranoia. In C++, destructor will abort() if it was called while unwinding
+ * the stack and it throws an exception.
+ */
+ try
+ {
+ Close();
+ m_sDebugDumpDir.clear();
+ }
+ catch (...)
+ {
+ error_msg_and_die("Internal error");
+ }
+}
+
+void CDebugDump::Open(const char *pDir)
+{
+ if (m_bOpened)
+ {
+ throw CABRTException(EXCEP_ERROR, "CDebugDump is already opened");
+ }
+ m_sDebugDumpDir = RemoveBackSlashes(pDir);
+ if (!ExistFileDir(m_sDebugDumpDir.c_str()))
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "'%s' does not exist", m_sDebugDumpDir.c_str());
+ }
+ Lock();
+ m_bOpened = true;
+ /* In case caller would want to create more files, he'll need uid:gid */
+ m_uid = 0;
+ m_gid = 0;
+ struct stat stat_buf;
+ if (stat(m_sDebugDumpDir.c_str(), &stat_buf) == 0)
+ {
+ m_uid = stat_buf.st_uid;
+ m_gid = stat_buf.st_gid;
+ }
+}
+
+bool CDebugDump::Exist(const char* pPath)
+{
+ std::string fullPath = concat_path_file(m_sDebugDumpDir.c_str(), pPath);
+ return ExistFileDir(fullPath.c_str());
+}
+
+static bool GetAndSetLock(const char* pLockFile, const char* pPID)
+{
+ while (symlink(pPID, pLockFile) != 0)
+ {
+ if (errno != EEXIST)
+ perror_msg_and_die("Can't create lock file '%s'", pLockFile);
+
+ char pid_buf[sizeof(pid_t)*3 + 4];
+ ssize_t r = readlink(pLockFile, pid_buf, sizeof(pid_buf) - 1);
+ if (r < 0)
+ {
+ if (errno == ENOENT)
+ {
+ /* Looks like pLockFile was deleted */
+ usleep(10 * 1000); /* avoid CPU eating loop */
+ continue;
+ }
+ perror_msg_and_die("Can't read lock file '%s'", pLockFile);
+ }
+ pid_buf[r] = '\0';
+
+ if (strcmp(pid_buf, pPID) == 0)
+ {
+ log("Lock file '%s' is already locked by us", pLockFile);
+ return false;
+ }
+ if (isdigit_str(pid_buf))
+ {
+ if (access(ssprintf("/proc/%s", pid_buf).c_str(), F_OK) == 0)
+ {
+ log("Lock file '%s' is locked by process %s", pLockFile, pid_buf);
+ return false;
+ }
+ log("Lock file '%s' was locked by process %s, but it crashed?", pLockFile, pid_buf);
+ }
+ /* The file may be deleted by now by other process. Ignore ENOENT */
+ if (unlink(pLockFile) != 0 && errno != ENOENT)
+ {
+ perror_msg_and_die("Can't remove stale lock file '%s'", pLockFile);
+ }
+ }
+
+ VERB1 log("Locked '%s'", pLockFile);
+ return true;
+
+#if 0
+/* Old code was using ordinary files instead of symlinks,
+ * but it had a race window between open and write, during which file was
+ * empty. It was seen to happen in practice.
+ */
+ int fd;
+ while ((fd = open(pLockFile, O_WRONLY | O_CREAT | O_EXCL, 0640)) < 0)
+ {
+ if (errno != EEXIST)
+ perror_msg_and_die("Can't create lock file '%s'", pLockFile);
+ fd = open(pLockFile, O_RDONLY);
+ if (fd < 0)
+ {
+ if (errno == ENOENT)
+ continue; /* someone else deleted the file */
+ perror_msg_and_die("Can't open lock file '%s'", pLockFile);
+ }
+ char pid_buf[sizeof(pid_t)*3 + 4];
+ int r = read(fd, pid_buf, sizeof(pid_buf) - 1);
+ if (r < 0)
+ perror_msg_and_die("Can't read lock file '%s'", pLockFile);
+ close(fd);
+ if (r == 0)
+ {
+ /* Other process did not write out PID yet.
+ * We HOPE it did not crash... */
+ continue;
+ }
+ pid_buf[r] = '\0';
+ if (strcmp(pid_buf, pPID) == 0)
+ {
+ log("Lock file '%s' is already locked by us", pLockFile);
+ return -1;
+ }
+ if (isdigit_str(pid_buf))
+ {
+ if (access(ssprintf("/proc/%s", pid_buf).c_str(), F_OK) == 0)
+ {
+ log("Lock file '%s' is locked by process %s", pLockFile, pid_buf);
+ return -1;
+ }
+ log("Lock file '%s' was locked by process %s, but it crashed?", pLockFile, pid_buf);
+ }
+ /* The file may be deleted by now by other process. Ignore errors */
+ unlink(pLockFile);
+ }
+
+ int len = strlen(pPID);
+ if (write(fd, pPID, len) != len)
+ {
+ unlink(pLockFile);
+ /* close(fd); - not needed, exiting does it too */
+ perror_msg_and_die("Can't write lock file '%s'", pLockFile);
+ }
+ close(fd);
+
+ VERB1 log("Locked '%s'", pLockFile);
+ return true;
+#endif
+}
+
+void CDebugDump::Lock()
+{
+ if (m_bLocked)
+ error_msg_and_die("Locking bug on '%s'", m_sDebugDumpDir.c_str());
+
+ std::string lockFile = m_sDebugDumpDir + ".lock";
+ char pid_buf[sizeof(long)*3 + 2];
+ sprintf(pid_buf, "%lu", (long)getpid());
+ while ((m_bLocked = GetAndSetLock(lockFile.c_str(), pid_buf)) != true)
+ {
+ sleep(1); /* was 0.5 seconds */
+ }
+}
+
+void CDebugDump::UnLock()
+{
+ if (m_bLocked)
+ {
+ m_bLocked = false;
+ std::string lockFile = m_sDebugDumpDir + ".lock";
+ xunlink(lockFile.c_str());
+ VERB1 log("UnLocked '%s'", lockFile.c_str());
+ }
+}
+
+/* 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.
+ */
+void CDebugDump::Create(const char *pDir, uid_t uid)
+{
+ if (m_bOpened)
+ {
+ throw CABRTException(EXCEP_ERROR, "DebugDump is already opened");
+ }
+
+ m_sDebugDumpDir = RemoveBackSlashes(pDir);
+ if (ExistFileDir(m_sDebugDumpDir.c_str()))
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "'%s' already exists", m_sDebugDumpDir.c_str());
+ }
+
+ Lock();
+ m_bOpened = true;
+
+ /* 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(m_sDebugDumpDir.c_str(), 0750) == -1)
+ {
+ UnLock();
+ m_bOpened = false;
+ throw CABRTException(EXCEP_DD_OPEN, "Can't create dir '%s'", pDir);
+ }
+
+ /* mkdir's mode (above) can be affected by umask, fix it */
+ if (chmod(m_sDebugDumpDir.c_str(), 0750) == -1)
+ {
+ UnLock();
+ m_bOpened = false;
+ throw CABRTException(EXCEP_DD_OPEN, "Can't change mode of '%s'", pDir);
+ }
+
+ /* Get ABRT's user id */
+ m_uid = 0;
+ struct passwd *pw = getpwnam("abrt");
+ if (pw)
+ m_uid = pw->pw_uid;
+ else
+ error_msg("User 'abrt' does not exist, using uid 0");
+
+ /* Get crashed application's group id */
+ m_gid = 0;
+ pw = getpwuid(uid);
+ if (pw)
+ m_gid = pw->pw_gid;
+ else
+ error_msg("User %lu does not exist, using gid 0", (long)uid);
+
+ if (chown(m_sDebugDumpDir.c_str(), m_uid, m_gid) == -1)
+ {
+ perror_msg("can't change '%s' ownership to %lu:%lu", m_sDebugDumpDir.c_str(),
+ (long)m_uid, (long)m_gid);
+ }
+
+ SaveText(CD_UID, to_string(uid).c_str());
+
+ {
+ struct utsname buf;
+ if (uname(&buf) != 0)
+ {
+ perror_msg_and_die("uname");
+ }
+ SaveText(FILENAME_KERNEL, buf.release);
+ SaveText(FILENAME_ARCHITECTURE, buf.machine);
+ std::string release;
+ LoadTextFile("/etc/redhat-release", release);
+ const char *release_ptr = release.c_str();
+ unsigned len_1st_str = strchrnul(release_ptr, '\n') - release_ptr;
+ release.erase(len_1st_str); /* usually simply removes trailing '\n' */
+ SaveText(FILENAME_RELEASE, release.c_str());
+ }
+
+ time_t t = time(NULL);
+ SaveText(FILENAME_TIME, to_string(t).c_str());
+}
+
+static void DeleteFileDir(const char *pDir)
+{
+ DIR *dir = opendir(pDir);
+ if (!dir)
+ return;
+
+ struct dirent *dent;
+ while ((dent = readdir(dir)) != NULL)
+ {
+ if (dot_or_dotdot(dent->d_name))
+ continue;
+ std::string fullPath = concat_path_file(pDir, dent->d_name);
+ if (unlink(fullPath.c_str()) == -1)
+ {
+ if (errno != EISDIR)
+ {
+ closedir(dir);
+ throw CABRTException(EXCEP_DD_DELETE, "Can't remove dir %s", fullPath.c_str());
+ }
+ DeleteFileDir(fullPath.c_str());
+ }
+ }
+ closedir(dir);
+ if (rmdir(pDir) == -1)
+ {
+ throw CABRTException(EXCEP_DD_DELETE, "Can't remove dir %s", pDir);
+ }
+}
+
+void CDebugDump::Delete()
+{
+ if (!ExistFileDir(m_sDebugDumpDir.c_str()))
+ {
+ return;
+ }
+ DeleteFileDir(m_sDebugDumpDir.c_str());
+}
+
+void CDebugDump::Close()
+{
+ UnLock();
+ if (m_pGetNextFileDir != NULL)
+ {
+ closedir(m_pGetNextFileDir);
+ m_pGetNextFileDir = NULL;
+ }
+ m_bOpened = false;
+}
+
+static void LoadTextFile(const char *pPath, std::string& pData)
+{
+ FILE *fp = fopen(pPath, "r");
+ if (!fp)
+ {
+ throw CABRTException(EXCEP_DD_LOAD, "Can't open file '%s'", pPath);
+ }
+ pData = "";
+ int ch;
+ while ((ch = fgetc(fp)) != EOF)
+ {
+ if (ch == '\0')
+ {
+ pData += ' ';
+ }
+ else if (isspace(ch) || (isascii(ch) && !iscntrl(ch)))
+ {
+ pData += ch;
+ }
+ }
+ fclose(fp);
+}
+
+static void SaveBinaryFile(const char *pPath, const char* pData, unsigned pSize, uid_t uid, gid_t gid)
+{
+ /* "Why 0640?!" See ::Create() for security analysis */
+ unlink(pPath);
+ int fd = open(pPath, O_WRONLY | O_TRUNC | O_CREAT, 0640);
+ if (fd < 0)
+ {
+ throw CABRTException(EXCEP_DD_SAVE, "Can't open file '%s': %s", pPath, errno ? strerror(errno) : "errno == 0");
+ }
+ if (fchown(fd, uid, gid) == -1)
+ {
+ perror_msg("can't change '%s' ownership to %lu:%lu", pPath, (long)uid, (long)gid);
+ }
+ unsigned r = full_write(fd, pData, pSize);
+ close(fd);
+ if (r != pSize)
+ {
+ throw CABRTException(EXCEP_DD_SAVE, "Can't save file '%s'", pPath);
+ }
+}
+
+void CDebugDump::LoadText(const char* pName, std::string& pData)
+{
+ if (!m_bOpened)
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "DebugDump is not opened");
+ }
+ std::string fullPath = concat_path_file(m_sDebugDumpDir.c_str(), pName);
+ LoadTextFile(fullPath.c_str(), pData);
+}
+
+void CDebugDump::SaveText(const char* pName, const char* pData)
+{
+ if (!m_bOpened)
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "DebugDump is not opened");
+ }
+ std::string fullPath = concat_path_file(m_sDebugDumpDir.c_str(), pName);
+ SaveBinaryFile(fullPath.c_str(), pData, strlen(pData), m_uid, m_gid);
+}
+
+void CDebugDump::SaveBinary(const char* pName, const char* pData, unsigned pSize)
+{
+ if (!m_bOpened)
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "DebugDump is not opened");
+ }
+ std::string fullPath = concat_path_file(m_sDebugDumpDir.c_str(), pName);
+ SaveBinaryFile(fullPath.c_str(), pData, pSize, m_uid, m_gid);
+}
+
+void CDebugDump::InitGetNextFile()
+{
+ if (!m_bOpened)
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "DebugDump is not opened");
+ }
+ if (m_pGetNextFileDir != NULL)
+ {
+ closedir(m_pGetNextFileDir);
+ }
+ m_pGetNextFileDir = opendir(m_sDebugDumpDir.c_str());
+ if (m_pGetNextFileDir == NULL)
+ {
+ throw CABRTException(EXCEP_DD_OPEN, "Can't open dir '%s'", m_sDebugDumpDir.c_str());
+ }
+}
+
+bool CDebugDump::GetNextFile(std::string *short_name, std::string *full_name)
+{
+ if (m_pGetNextFileDir == NULL)
+ {
+ return false;
+ }
+
+ struct dirent *dent;
+ while ((dent = readdir(m_pGetNextFileDir)) != NULL)
+ {
+ if (is_regular_file(dent, m_sDebugDumpDir.c_str()))
+ {
+ if (short_name)
+ *short_name = dent->d_name;
+ if (full_name)
+ *full_name = concat_path_file(m_sDebugDumpDir.c_str(), dent->d_name);
+ return true;
+ }
+ }
+ closedir(m_pGetNextFileDir);
+ m_pGetNextFileDir = NULL;
+ return false;
+}
+
+/* Utility function */
+void delete_debug_dump_dir(const char *pDebugDumpDir)
+{
+ try
+ {
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ dd.Delete();
+ }
+ catch (CABRTException& e)
+ {
+ /* Ignoring "directory already deleted" and such */
+ }
+}
diff --git a/lib/utils/Makefile.am b/lib/utils/Makefile.am
new file mode 100644
index 00000000..04211a7c
--- /dev/null
+++ b/lib/utils/Makefile.am
@@ -0,0 +1,86 @@
+# 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
+lib_LTLIBRARIES = libABRTUtils.la libABRTdUtils.la
+HEADER_DIR = $(srcdir)/../../inc
+AM_CPPFLAGS = -I$(HEADER_DIR)
+AM_YFLAGS = --verbose
+
+# Not used just yet:
+# time.cpp
+# xconnect.cpp
+
+libABRTUtils_la_SOURCES = \
+ xfuncs.c \
+ encbase64.cpp \
+ read_write.c read_write.h \
+ logging.c logging.h \
+ copyfd.cpp \
+ daemon.cpp \
+ skip_whitespace.cpp \
+ xatonum.cpp numtoa.cpp \
+ spawn.cpp \
+ stringops.cpp \
+ dirsize.cpp \
+ DebugDump.cpp \
+ abrt_dbus.h abrt_dbus.cpp \
+ CrashTypes.cpp \
+ ABRTException.cpp \
+ backtrace.h backtrace.c \
+ backtrace_parser.y \
+ strbuf.h strbuf.c \
+ abrt_packages.c abrt_packages.h \
+ hooklib.h hooklib.cpp
+libABRTUtils_la_CPPFLAGS = \
+ -Wall -Werror \
+ -I$(srcdir)/../../inc \
+ -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)/abrt_xmlrpc.h abrt_xmlrpc.cpp \
+ abrt_rh_support.h abrt_rh_support.cpp \
+ abrt_curl.h abrt_curl.cpp \
+ $(HEADER_DIR)/plugin.h Plugin.cpp \
+ Polkit.h Polkit.cpp
+
+libABRTdUtils_la_CPPFLAGS = \
+ -Wall -Werror \
+ -I$(srcdir)/../../inc \
+ -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)\" \
+ $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
+ $(POLKIT_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ -D_GNU_SOURCE
+libABRTdUtils_la_LDFLAGS = \
+ -version-info 0:1:0
+libABRTdUtils_la_LIBADD = \
+ $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \
+ $(POLKIT_LIBS) \
+ $(LIBXML_LIBS) \
+ -ldl
+
+$(DESTDIR)/$(DEBUG_DUMPS_DIR):
+ $(mkdir_p) '$@'
+# no need to chmod it here
+#chmod 1777 '$@'
+install-data-local: $(DESTDIR)/$(DEBUG_DUMPS_DIR)
diff --git a/lib/utils/Plugin.cpp b/lib/utils/Plugin.cpp
new file mode 100644
index 00000000..40fa39de
--- /dev/null
+++ b/lib/utils/Plugin.cpp
@@ -0,0 +1,104 @@
+/*
+ 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;
+}
+
+const map_plugin_settings_t& CPlugin::GetSettings()
+{
+ return m_pSettings;
+}
+
+bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings,
+ bool skipKeysWithoutValue /*= true*/)
+{
+ FILE *fp = fopen(pPath, "r");
+ if (!fp)
+ return false;
+
+ char line[512];
+ while (fgets(line, sizeof(line), fp))
+ {
+ strchrnul(line, '\n')[0] = '\0';
+ 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)
+ continue;
+
+ /* Skip lines with empty key. */
+ if (key.length() == 0)
+ continue;
+
+ if (skipKeysWithoutValue && value.length() == 0)
+ continue;
+
+ /* Skip lines with unclosed quotes. */
+ if (in_quote)
+ continue;
+
+ pSettings[key] = value;
+ }
+ fclose(fp);
+ return true;
+}
diff --git a/lib/utils/Polkit.cpp b/lib/utils/Polkit.cpp
new file mode 100644
index 00000000..a5e07760
--- /dev/null
+++ b/lib/utils/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/lib/utils/Polkit.h b/lib/utils/Polkit.h
new file mode 100644
index 00000000..d9e097ac
--- /dev/null
+++ b/lib/utils/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/lib/utils/abrt_curl.cpp b/lib/utils/abrt_curl.cpp
new file mode 100644
index 00000000..af3defca
--- /dev/null
+++ b/lib/utils/abrt_curl.cpp
@@ -0,0 +1,320 @@
+/*
+ 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"
+#include "comm_layer_inner.h"
+
+using namespace std;
+
+/*
+ * 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;
+}
+
+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);
+ } else if (data_size == ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) {
+ // ...from a file, in multipart/formdata format
+ CURLFORMcode curlform_err = curl_formadd(&post, &last,
+ CURLFORM_PTRNAME, "file",
+ CURLFORM_FILE, data, // filename to read from
+ CURLFORM_CONTENTTYPE, content_type,
+ CURLFORM_FILENAME, data, // filename to put in the form
+ CURLFORM_END);
+ if (curlform_err != 0)
+//FIXME:
+ error_msg_and_die("out of memory or read error");
+ 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);
+ }
+
+ // 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(CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+
+ // 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/lib/utils/abrt_curl.h b/lib/utils/abrt_curl.h
new file mode 100644
index 00000000..1f34e7ec
--- /dev/null
+++ b/lib/utils/abrt_curl.h
@@ -0,0 +1,88 @@
+/*
+ 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>
+
+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);
+}
+
+#endif
diff --git a/lib/utils/abrt_dbus.cpp b/lib/utils/abrt_dbus.cpp
new file mode 100644
index 00000000..4319d103
--- /dev/null
+++ b/lib/utils/abrt_dbus.cpp
@@ -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!" */
+}
+
+struct watch_app_info_t
+{
+ GIOChannel *channel;
+ guint event_source_id;
+ bool watch_enabled;
+};
+/* 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/lib/utils/abrt_dbus.h b/lib/utils/abrt_dbus.h
new file mode 100644
index 00000000..58a8d912
--- /dev/null
+++ b/lib/utils/abrt_dbus.h
@@ -0,0 +1,302 @@
+/*
+ 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_UTIL_DBUS_H
+#define ABRT_UTIL_DBUS_H
+
+#include <dbus/dbus.h>
+#include <map>
+#include <vector>
+#include "abrtlib.h"
+
+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);
+ * // 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);
+ * 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 = NULL,
+ /* makes sense only if you use object_path_to_register: */
+ DBusHandlerResult (*message_received_func)(DBusConnection *conn, DBusMessage *msg, void* data) = NULL
+);
+
+
+/*
+ * 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);
+
+//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
+ */
+
+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);
+//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
diff --git a/lib/utils/abrt_packages.c b/lib/utils/abrt_packages.c
new file mode 100644
index 00000000..d9ffe44a
--- /dev/null
+++ b/lib/utils/abrt_packages.c
@@ -0,0 +1,44 @@
+/*
+ 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_packages.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/lib/utils/abrt_packages.h b/lib/utils/abrt_packages.h
new file mode 100644
index 00000000..e6209d81
--- /dev/null
+++ b/lib/utils/abrt_packages.h
@@ -0,0 +1,35 @@
+/*
+ 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
+
+#include "xfuncs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char* get_package_name_from_NVR_or_NULL(const char* packageNVR);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/utils/abrt_rh_support.cpp b/lib/utils/abrt_rh_support.cpp
new file mode 100644
index 00000000..7e804f9f
--- /dev/null
+++ b/lib/utils/abrt_rh_support.cpp
@@ -0,0 +1,513 @@
+/*
+ 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.
+*/
+//#define _GNU_SOURCE
+
+#include <libxml/encoding.h>
+#include <libxml/xmlwriter.h>
+#include <curl/curl.h>
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "abrt_xmlrpc.h"
+#include "abrt_exception.h"
+#include "abrt_rh_support.h"
+
+using namespace std;
+
+struct reportfile {
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+};
+
+static void __attribute__((__noreturn__))
+die_xml_oom(void)
+{
+ error_msg_and_die("can't create XML attribute (out of memory?)");
+}
+
+static xmlBufferPtr
+xxmlBufferCreate(void)
+{
+ xmlBufferPtr r = xmlBufferCreate();
+ if (!r)
+ die_xml_oom();
+ return r;
+}
+
+static xmlTextWriterPtr
+xxmlNewTextWriterMemory(xmlBufferPtr buf /*, int compression*/)
+{
+ xmlTextWriterPtr r = xmlNewTextWriterMemory(buf, /*compression:*/ 0);
+ if (!r)
+ die_xml_oom();
+ return r;
+}
+
+static void
+xxmlTextWriterStartDocument(xmlTextWriterPtr writer,
+ const char * version,
+ const char * encoding,
+ const char * standalone)
+{
+ if (xmlTextWriterStartDocument(writer, version, encoding, standalone) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterEndDocument(xmlTextWriterPtr writer)
+{
+ if (xmlTextWriterEndDocument(writer) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterStartElement(xmlTextWriterPtr writer, const char *name)
+{
+ // these bright guys REDEFINED CHAR (!) to unsigned char...
+ if (xmlTextWriterStartElement(writer, (unsigned char*)name) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterEndElement(xmlTextWriterPtr writer)
+{
+ if (xmlTextWriterEndElement(writer) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterWriteElement(xmlTextWriterPtr writer, const char *name, const char *content)
+{
+ if (xmlTextWriterWriteElement(writer, (unsigned char*)name, (unsigned char*)content) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterWriteAttribute(xmlTextWriterPtr writer, const char *name, const char *content)
+{
+ if (xmlTextWriterWriteAttribute(writer, (unsigned char*)name, (unsigned char*)content) < 0)
+ die_xml_oom();
+}
+
+#if 0 //unused
+static void
+xxmlTextWriterWriteString(xmlTextWriterPtr writer, const char *content)
+{
+ if (xmlTextWriterWriteString(writer, (unsigned char*)content) < 0)
+ die_xml_oom();
+}
+#endif
+
+//
+// End the reportfile, and prepare it for delivery.
+// No more bindings can be added after this.
+//
+static void
+close_writer(reportfile_t* file)
+{
+ if (!file->writer)
+ return;
+
+ // close off the end of the xml file
+ xxmlTextWriterEndDocument(file->writer);
+ xmlFreeTextWriter(file->writer);
+ file->writer = NULL;
+}
+
+//
+// This allocates a reportfile_t structure and initializes it.
+//
+reportfile_t*
+new_reportfile(void)
+{
+ // create a new reportfile_t
+ reportfile_t* file = (reportfile_t*)xmalloc(sizeof(*file));
+
+ // set up a libxml 'buffer' and 'writer' to that buffer
+ file->buf = xxmlBufferCreate();
+ file->writer = xxmlNewTextWriterMemory(file->buf);
+
+ // start a new xml document:
+ // <report xmlns="http://www.redhat.com/gss/strata">...
+ xxmlTextWriterStartDocument(file->writer, /*version:*/ NULL, /*encoding:*/ NULL, /*standalone:*/ NULL);
+ xxmlTextWriterStartElement(file->writer, "report");
+ xxmlTextWriterWriteAttribute(file->writer, "xmlns", "http://www.redhat.com/gss/strata");
+
+ return file;
+}
+
+static void
+internal_reportfile_start_binding(reportfile_t* file, const char* name, int isbinary, const char* filename)
+{
+ // <binding name=NAME [fileName=FILENAME] type=text/binary...
+ xxmlTextWriterStartElement(file->writer, "binding");
+ xxmlTextWriterWriteAttribute(file->writer, "name", name);
+ if (filename)
+ xxmlTextWriterWriteAttribute(file->writer, "fileName", filename);
+ if (isbinary)
+ xxmlTextWriterWriteAttribute(file->writer, "type", "binary");
+ else
+ xxmlTextWriterWriteAttribute(file->writer, "type", "text");
+}
+
+//
+// Add a new text binding
+//
+void
+reportfile_add_binding_from_string(reportfile_t* file, const char* name, const char* value)
+{
+ // <binding name=NAME type=text value=VALUE>
+ internal_reportfile_start_binding(file, name, /*isbinary:*/ 0, /*filename:*/ NULL);
+ xxmlTextWriterWriteAttribute(file->writer, "value", value);
+ xxmlTextWriterEndElement(file->writer);
+}
+
+//
+// Add a new binding to a report whose value is represented as a file.
+//
+void
+reportfile_add_binding_from_namedfile(reportfile_t* file,
+ const char* on_disk_filename, /* unused so far */
+ const char* binding_name,
+ const char* recorded_filename,
+ int isbinary)
+{
+ // <binding name=NAME fileName=FILENAME type=text/binary...
+ internal_reportfile_start_binding(file, binding_name, isbinary, recorded_filename);
+ // ... href=content/NAME>
+ string href_name = concat_path_file("content", binding_name);
+ xxmlTextWriterWriteAttribute(file->writer, "href", href_name.c_str());
+}
+
+//
+// Return the contents of the reportfile as a string.
+//
+const char*
+reportfile_as_string(reportfile_t* file)
+{
+ close_writer(file);
+ // unsigned char -> char
+ return (char*)file->buf->content;
+}
+
+void
+reportfile_free(reportfile_t* file)
+{
+ if (!file)
+ return;
+ close_writer(file);
+ xmlBufferFree(file->buf);
+ free(file);
+}
+
+
+//
+// post_signature()
+//
+char*
+post_signature(const char* baseURL, bool ssl_verify, const char* signature)
+{
+ string URL = concat_path_file(baseURL, "/signatures");
+
+ abrt_post_state *state = new_abrt_post_state(0
+ + ABRT_POST_WANT_HEADERS
+ + ABRT_POST_WANT_BODY
+ + ABRT_POST_WANT_ERROR_MSG
+ + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
+ );
+ int http_resp_code = abrt_post_string(state, URL.c_str(), "application/xml", signature);
+
+ char *retval;
+ const char *strata_msg;
+ switch (http_resp_code)
+ {
+ case 200:
+ case 201:
+ if (state->body)
+ {
+ retval = state->body;
+ state->body = NULL;
+ break;
+ }
+ strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:");
+ if (strata_msg && strcmp(strata_msg, "CREATED") != 0) {
+ retval = xstrdup(strata_msg);
+ break;
+ }
+ retval = xstrdup("Signature submitted successfully");
+ break;
+
+ default:
+ strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:");
+ if (strata_msg)
+ {
+ retval = xasprintf("Error (HTTP response %d): %s",
+ http_resp_code,
+ strata_msg);
+ break;
+ }
+ if (state->curl_error_msg)
+ {
+ if (http_resp_code >= 0)
+ retval = xasprintf("Error (HTTP response %d): %s", http_resp_code, state->curl_error_msg);
+ else
+ retval = xasprintf("Error in HTTP transaction: %s", state->curl_error_msg);
+ break;
+ }
+ retval = xasprintf("Error (HTTP response %d), body:\n%s", http_resp_code, state->body);
+ break;
+ }
+
+ free_abrt_post_state(state);
+ return retval;
+}
+
+
+//
+// send_report_to_new_case()
+//
+
+static char*
+make_case_data(const char* summary, const char* description,
+ const char* product, const char* version,
+ const char* component)
+{
+ char* retval;
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xxmlBufferCreate();
+ writer = xxmlNewTextWriterMemory(buf);
+
+ xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes");
+ xxmlTextWriterStartElement(writer, "case");
+ xxmlTextWriterWriteAttribute(writer, "xmlns",
+ "http://www.redhat.com/gss/strata");
+
+ xxmlTextWriterWriteElement(writer, "summary", summary);
+ xxmlTextWriterWriteElement(writer, "description", description);
+ if (product) {
+ xxmlTextWriterWriteElement(writer, "product", product);
+ }
+ if (version) {
+ xxmlTextWriterWriteElement(writer, "version", version);
+ }
+ if (component) {
+ xxmlTextWriterWriteElement(writer, "component", component);
+ }
+
+ xxmlTextWriterEndDocument(writer);
+ retval = xstrdup((const char*)buf->content);
+ xmlFreeTextWriter(writer);
+ xmlBufferFree(buf);
+ return retval;
+}
+
+#if 0 //unused
+static char*
+make_response(const char* title, const char* body,
+ const char* actualURL, const char* displayURL)
+{
+ char* retval;
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xxmlBufferCreate();
+ writer = xxmlNewTextWriterMemory(buf);
+
+ xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes");
+ xxmlTextWriterStartElement(writer, "response");
+ if (title) {
+ xxmlTextWriterWriteElement(writer, "title", title);
+ }
+ if (body) {
+ xxmlTextWriterWriteElement(writer, "body", body);
+ }
+ if (actualURL || displayURL) {
+ xxmlTextWriterStartElement(writer, "URL");
+ if (actualURL) {
+ xxmlTextWriterWriteAttribute(writer, "href", actualURL);
+ }
+ if (displayURL) {
+ xxmlTextWriterWriteString(writer, displayURL);
+ }
+ }
+
+ xxmlTextWriterEndDocument(writer);
+ retval = xstrdup((const char*)buf->content);
+ xmlFreeTextWriter(writer);
+ xmlBufferFree(buf);
+ return retval;
+}
+//Example:
+//<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+//<response><title>Case Created and Report Attached</title><body></body><URL href="http://support-services-devel.gss.redhat.com:8080/Strata/cases/00005129/attachments/ccbf3e65-b941-3db7-a016-6a3831691a32">New Case URL</URL></response>
+#endif
+
+char*
+send_report_to_new_case(const char* baseURL,
+ const char* username,
+ const char* password,
+ bool ssl_verify,
+ const char* summary,
+ const char* description,
+ const char* component,
+ const char* report_file_name)
+{
+ string case_url = concat_path_file(baseURL, "/cases");
+
+ char *case_data = make_case_data(summary, description,
+ "Red Hat Enterprise Linux", "6.0",
+ component);
+
+ int redirect_count = 0;
+ char *errmsg;
+ char *allocated = NULL;
+ char* retval = NULL;
+ abrt_post_state *case_state;
+
+ redirect_case:
+ case_state = new_abrt_post_state(0
+ + ABRT_POST_WANT_HEADERS
+ + ABRT_POST_WANT_BODY
+ + ABRT_POST_WANT_ERROR_MSG
+ + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
+ );
+ case_state->username = username;
+ case_state->password = password;
+ abrt_post_string(case_state, case_url.c_str(), "application/xml", case_data);
+
+ char *case_location = find_header_in_abrt_post_state(case_state, "Location:");
+ switch (case_state->http_resp_code)
+ {
+ case 305: /* "305 Use Proxy" */
+ if (++redirect_count < 10 && case_location) {
+ case_url = case_location;
+ free_abrt_post_state(case_state);
+ goto redirect_case;
+ }
+ goto bad_resp_code;
+
+ case 404:
+ /* Not strictly necessary, but makes this typical error less cryptic:
+ * instead of returning html-encoded body, we show short concise message,
+ * and show offending URL (typos in which is a typical cause) */
+ retval = xasprintf("error in case creation, "
+ "HTTP code: 404 (Not found), URL:'%s'", case_url.c_str());
+ break;
+
+ default:
+ bad_resp_code:
+ errmsg = case_state->curl_error_msg;
+ if (errmsg)
+ retval = xasprintf("error in case creation: %s", errmsg);
+ else
+ {
+ errmsg = find_header_in_abrt_post_state(case_state, "Strata-Message:");
+ if ((!errmsg || !errmsg[0]) && case_state->body && case_state->body[0])
+ errmsg = case_state->body;
+ if (errmsg)
+ retval = xasprintf("error in case creation, HTTP code: %d, server says: '%s'",
+ case_state->http_resp_code, errmsg);
+ else
+ retval = xasprintf("error in case creation, HTTP code: %d",
+ case_state->http_resp_code);
+ }
+ break;
+
+ case 200:
+ case 201:
+ if (!case_location) {
+ /* Case Creation returned valid code, but no location */
+ retval = xasprintf("error in case creation: no Location URL, HTTP code: %d",
+ case_state->http_resp_code);
+ break;
+ }
+
+ string atch_url = concat_path_file(case_location, "/attachments");
+ abrt_post_state *atch_state;
+ redirect_attach:
+ atch_state = new_abrt_post_state(0
+ + ABRT_POST_WANT_HEADERS
+ + ABRT_POST_WANT_BODY
+ + ABRT_POST_WANT_ERROR_MSG
+ + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
+ );
+ atch_state->username = username;
+ atch_state->password = password;
+ abrt_post_file_as_form(atch_state, atch_url.c_str(), "application/binary", report_file_name);
+
+ char *atch_location = find_header_in_abrt_post_state(atch_state, "Location:");
+ switch (atch_state->http_resp_code)
+ {
+ case 305: /* "305 Use Proxy" */
+ if (++redirect_count < 10 && atch_location) {
+ atch_url = atch_location;
+ free_abrt_post_state(atch_state);
+ goto redirect_attach;
+ }
+ /* fall through */
+
+ default:
+ /* Case Creation Succeeded, attachement FAILED */
+ errmsg = find_header_in_abrt_post_state(atch_state, "Strata-Message:");
+ if (!errmsg || !errmsg[0])
+ errmsg = atch_state->curl_error_msg;
+ if (atch_state->body && atch_state->body[0])
+ {
+ if (errmsg && errmsg[0]
+ && strcmp(errmsg, atch_state->body) != 0
+ ) /* both strata/curl error and body are present (and aren't the same) */
+ allocated = errmsg = xasprintf("%s. %s",
+ atch_state->body,
+ errmsg);
+ else /* only body exists */
+ errmsg = atch_state->body;
+ }
+ /* Note: to prevent URL misparsing, make sure to delimit
+ * case_location only using spaces */
+ retval = xasprintf("Case created: %s but report attachment failed (HTTP code %d)%s%s",
+ case_location,
+ atch_state->http_resp_code,
+ errmsg ? ": " : "",
+ errmsg ? errmsg : ""
+ );
+ break;
+
+ case 200:
+ case 201:
+ // unused
+ //char *body = atch_state->body;
+ //if (case_state->body && case_state->body[0])
+ //{
+ // body = case_state->body;
+ // if (atch_state->body && atch_state->body[0])
+ // allocated = body = xasprintf("%s\n%s",
+ // case_state->body,
+ // atch_state->body);
+ //}
+ retval = xasprintf("Case created: %s", /*body,*/ case_location);
+ } /* switch (attach HTTP code) */
+ free_abrt_post_state(atch_state);
+
+ } /* switch (case HTTP code) */
+
+ free_abrt_post_state(case_state);
+ free(allocated);
+ return retval;
+}
diff --git a/lib/utils/abrt_rh_support.h b/lib/utils/abrt_rh_support.h
new file mode 100644
index 00000000..393a1a2c
--- /dev/null
+++ b/lib/utils/abrt_rh_support.h
@@ -0,0 +1,47 @@
+/*
+ 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_RH_SUPPORT_H_
+#define ABRT_RH_SUPPORT_H_ 1
+
+typedef struct reportfile reportfile_t;
+
+reportfile_t *new_reportfile(void);
+void reportfile_free(reportfile_t* file);
+
+void reportfile_add_binding_from_string(reportfile_t* file, const char* name, const char* value);
+void reportfile_add_binding_from_namedfile(reportfile_t* file,
+ const char* on_disk_filename, /* unused so far */
+ const char* binding_name,
+ const char* recorded_filename,
+ int isbinary);
+
+const char* reportfile_as_string(reportfile_t* file);
+
+char* post_signature(const char* baseURL, bool ssl_verify, const char* signature);
+char*
+send_report_to_new_case(const char* baseURL,
+ const char* username,
+ const char* password,
+ bool ssl_verify,
+ const char* summary,
+ const char* description,
+ const char* component,
+ const char* report_file_name);
+
+#endif
diff --git a/lib/utils/abrt_xmlrpc.cpp b/lib/utils/abrt_xmlrpc.cpp
new file mode 100644
index 00000000..ac2654ed
--- /dev/null
+++ b/lib/utils/abrt_xmlrpc.cpp
@@ -0,0 +1,100 @@
+/*
+ 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.
+*/
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+#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/lib/utils/backtrace.c b/lib/utils/backtrace.c
new file mode 100644
index 00000000..a2efa766
--- /dev/null
+++ b/lib/utils/backtrace.c
@@ -0,0 +1,846 @@
+/*
+ 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 "backtrace.h"
+#include "strbuf.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "xfuncs.h"
+
+struct frame *frame_new()
+{
+ struct frame *f = malloc(sizeof(struct frame));
+ if (!f)
+ {
+ puts("Error while allocating memory for backtrace frame.");
+ exit(5);
+ }
+
+ f->function = NULL;
+ f->number = 0;
+ f->sourcefile = NULL;
+ f->signal_handler_called = false;
+ f->next = NULL;
+ return f;
+}
+
+void frame_free(struct frame *f)
+{
+ if (f->function)
+ free(f->function);
+ if (f->sourcefile)
+ free(f->sourcefile);
+ free(f);
+}
+
+struct frame *frame_add_sibling(struct frame *a, struct frame *b)
+{
+ struct frame *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+/* Appends a string representation of 'frame' to the 'str'. */
+static void frame_append_str(struct frame *frame, struct strbuf *str, bool verbose)
+{
+ if (verbose)
+ strbuf_append_strf(str, " #%d", frame->number);
+ else
+ strbuf_append_str(str, " ");
+
+ if (frame->function)
+ strbuf_append_strf(str, " %s", frame->function);
+ if (verbose && frame->sourcefile)
+ {
+ if (frame->function)
+ strbuf_append_str(str, " at");
+ strbuf_append_strf(str, " %s", frame->sourcefile);
+ }
+
+ if (frame->signal_handler_called)
+ strbuf_append_str(str, " <signal handler called>");
+
+ strbuf_append_str(str, "\n");
+}
+
+static bool frame_is_exit_handler(struct frame *frame)
+{
+ return (frame->function
+ && frame->sourcefile
+ && 0 == strcmp(frame->function, "__run_exit_handlers")
+ && NULL != strstr(frame->sourcefile, "exit.c"));
+}
+
+/* Checks if a frame contains abort function used
+ * by operating system to exit application.
+ * E.g. in C it's called "abort" or "raise".
+ */
+static bool frame_is_abort_frame(struct frame *frame)
+{
+ if (!frame->function || !frame->sourcefile)
+ return false;
+
+ if (0 == strcmp(frame->function, "raise")
+ && (NULL != strstr(frame->sourcefile, "pt-raise.c")
+ || NULL != strstr(frame->sourcefile, "/libc.so.6")))
+ return true;
+ else if (0 == strcmp(frame->function, "exit")
+ && NULL != strstr(frame->sourcefile, "exit.c"))
+ return true;
+ else if (0 == strcmp(frame->function, "abort")
+ && (NULL != strstr(frame->sourcefile, "abort.c")
+ || NULL != strstr(frame->sourcefile, "/libc.so.6")))
+ return true;
+ else if (frame_is_exit_handler(frame))
+ return true;
+
+ return false;
+}
+
+static bool frame_is_noncrash_frame(struct frame *frame)
+{
+ /* Abort frames. */
+ if (frame_is_abort_frame(frame))
+ return true;
+
+ if (!frame->function)
+ return false;
+
+ if (0 == strcmp(frame->function, "__kernel_vsyscall"))
+ return true;
+
+ if (0 == strcmp(frame->function, "__assert_fail"))
+ return true;
+
+ if (!frame->sourcefile)
+ return false;
+
+ /* GDK */
+ if (0 == strcmp(frame->function, "gdk_x_error")
+ && 0 == strcmp(frame->sourcefile, "gdkmain-x11.c"))
+ return true;
+
+ /* X.org */
+ if (0 == strcmp(frame->function, "_XReply")
+ && 0 == strcmp(frame->sourcefile, "xcb_io.c"))
+ return true;
+ if (0 == strcmp(frame->function, "_XError")
+ && 0 == strcmp(frame->sourcefile, "XlibInt.c"))
+ return true;
+ if (0 == strcmp(frame->function, "XSync")
+ && 0 == strcmp(frame->sourcefile, "Sync.c"))
+ return true;
+ if (0 == strcmp(frame->function, "process_responses")
+ && 0 == strcmp(frame->sourcefile, "xcb_io.c"))
+ return true;
+
+ /* glib */
+ if (0 == strcmp(frame->function, "IA__g_log")
+ && 0 == strcmp(frame->sourcefile, "gmessages.c"))
+ return true;
+ if (0 == strcmp(frame->function, "IA__g_logv")
+ && 0 == strcmp(frame->sourcefile, "gmessages.c"))
+ return true;
+ if (0 == strcmp(frame->function, "IA__g_assertion_message")
+ && 0 == strcmp(frame->sourcefile, "gtestutils.c"))
+ return true;
+ if (0 == strcmp(frame->function, "IA__g_assertion_message_expr")
+ && 0 == strcmp(frame->sourcefile, "gtestutils.c"))
+ return true;
+
+ /* DBus */
+ if (0 == strcmp(frame->function, "gerror_to_dbus_error_message")
+ && 0 == strcmp(frame->sourcefile, "dbus-gobject.c"))
+ return true;
+ if (0 == strcmp(frame->function, "dbus_g_method_return_error")
+ && 0 == strcmp(frame->sourcefile, "dbus-gobject.c"))
+ return true;
+
+ /* libstdc++ */
+ if (0 == strcmp(frame->function, "__gnu_cxx::__verbose_terminate_handler")
+ && NULL != strstr(frame->sourcefile, "/vterminate.cc"))
+ return true;
+ if (0 == strcmp(frame->function, "__cxxabiv1::__terminate")
+ && NULL != strstr(frame->sourcefile, "/eh_terminate.cc"))
+ return true;
+ if (0 == strcmp(frame->function, "std::terminate")
+ && NULL != strstr(frame->sourcefile, "/eh_terminate.cc"))
+ return true;
+ if (0 == strcmp(frame->function, "__cxxabiv1::__cxa_throw")
+ && NULL != strstr(frame->sourcefile, "/eh_throw.cc"))
+ return true;
+
+ return false;
+}
+
+struct thread *thread_new()
+{
+ struct thread *t = malloc(sizeof(struct thread));
+ if (!t)
+ {
+ puts("Error while allocating memory for backtrace thread.");
+ exit(5);
+ }
+
+ t->number = 0;
+ t->frames = NULL;
+ t->next = NULL;
+ return t;
+}
+
+void thread_free(struct thread *t)
+{
+ while (t->frames)
+ {
+ struct frame *rm = t->frames;
+ t->frames = rm->next;
+ frame_free(rm);
+ }
+
+ free(t);
+}
+
+struct thread *thread_add_sibling(struct thread *a, struct thread *b)
+{
+ struct thread *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+static int thread_get_frame_count(struct thread *thread)
+{
+ struct frame *f = thread->frames;
+ int count = 0;
+ while (f)
+ {
+ f = f->next;
+ ++count;
+ }
+ return count;
+}
+
+/* Appends string representation of 'thread' to the 'str'. */
+static void thread_append_str(struct thread *thread, struct strbuf *str, bool verbose)
+{
+ int framecount = thread_get_frame_count(thread);
+ if (verbose)
+ strbuf_append_strf(str, "Thread no. %d (%d frames)\n", thread->number, framecount);
+ else
+ strbuf_append_str(str, "Thread\n");
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ frame_append_str(frame, str, verbose);
+ frame = frame->next;
+ }
+}
+
+/*
+ * Checks whether the thread it contains some known "abort" function.
+ * If a frame with the function is found, it is returned.
+ * If there are multiple frames with abort function, the lowest
+ * one is returned.
+ * Nonrecursive.
+ */
+struct frame *thread_find_abort_frame(struct thread *thread)
+{
+ struct frame *frame = thread->frames;
+ struct frame *result = NULL;
+ while (frame)
+ {
+ if (frame_is_abort_frame(frame))
+ result = frame;
+
+ frame = frame->next;
+ }
+
+ return result;
+}
+
+static void thread_remove_exit_handlers(struct thread *thread)
+{
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ if (frame_is_exit_handler(frame))
+ {
+ /* Delete all frames from the beginning to this frame. */
+ while (thread->frames != frame)
+ {
+ struct frame *rm = thread->frames;
+ thread->frames = thread->frames->next;
+ frame_free(rm);
+ }
+ return;
+ }
+
+ frame = frame->next;
+ }
+}
+
+static void thread_remove_noncrash_frames(struct thread *thread)
+{
+ struct frame *prev = NULL;
+ struct frame *cur = thread->frames;
+ while (cur)
+ {
+ if (frame_is_noncrash_frame(cur))
+ {
+ /* This frame must be skipped, because it will
+ be deleted. */
+ if (prev)
+ prev->next = cur->next;
+ else
+ thread->frames = cur->next;
+
+ frame_free(cur);
+
+ /* Set cur to be valid, as it will be used to
+ advance to next item. */
+ if (prev)
+ cur = prev;
+ else
+ {
+ cur = thread->frames;
+ continue;
+ }
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+}
+
+/* Counts the number of quality frames and the number of all frames
+ * in a thread.
+ * @param ok_count
+ * @param all_count
+ * Not zeroed. This function just adds the numbers to
+ * ok_count and all_count.
+ */
+static void thread_rating(struct thread *thread, int *ok_count, int *all_count)
+{
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ *all_count += 1;
+ if (frame->signal_handler_called ||
+ (frame->function && 0 != strcmp(frame->function, "??")))
+ {
+ *ok_count += 1;
+ }
+ frame = frame->next;
+ }
+}
+
+struct backtrace *backtrace_new()
+{
+ struct backtrace *bt = malloc(sizeof(struct backtrace));
+ if (!bt)
+ {
+ puts("Error while allocating memory for backtrace.");
+ exit(5);
+ }
+
+ bt->threads = NULL;
+ bt->crash = NULL;
+ return bt;
+}
+
+void backtrace_free(struct backtrace *bt)
+{
+ while (bt->threads)
+ {
+ struct thread *rm = bt->threads;
+ bt->threads = rm->next;
+ thread_free(rm);
+ }
+
+ if (bt->crash)
+ frame_free(bt->crash);
+
+ free(bt);
+}
+
+static int backtrace_get_thread_count(struct backtrace *bt)
+{
+ struct thread *t = bt->threads;
+ int count = 0;
+ while (t)
+ {
+ t = t->next;
+ ++count;
+ }
+ return count;
+}
+
+void backtrace_print_tree(struct backtrace *backtrace, bool verbose)
+{
+ struct strbuf *strbuf = backtrace_tree_as_str(backtrace, verbose);
+ puts(strbuf->buf);
+ strbuf_free(strbuf);
+}
+
+struct strbuf *backtrace_tree_as_str(struct backtrace *backtrace, bool verbose)
+{
+ struct strbuf *str = strbuf_new();
+ if (verbose)
+ strbuf_append_strf(str, "Thread count: %d\n", backtrace_get_thread_count(backtrace));
+
+ if (backtrace->crash && verbose)
+ {
+ strbuf_append_str(str, "Crash frame: ");
+ frame_append_str(backtrace->crash, str, verbose);
+ }
+
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_append_str(thread, str, verbose);
+ thread = thread->next;
+ }
+
+ return str;
+}
+
+void backtrace_remove_threads_except_one(struct backtrace *backtrace,
+ struct thread *one)
+{
+ while (backtrace->threads)
+ {
+ struct thread *rm = backtrace->threads;
+ backtrace->threads = rm->next;
+ if (rm != one)
+ thread_free(rm);
+ }
+
+ one->next = NULL;
+ backtrace->threads = one;
+}
+
+/*
+ * Loop through all threads and if a single one contains the crash frame on the top,
+ * return it. Otherwise, return NULL.
+ *
+ * If require_abort is true, it is also required that the thread containing
+ * the crash frame contains some known "abort" function. In this case there can be
+ * multiple threads with the crash frame on the top, but only one of them might
+ * contain the abort function to succeed.
+ */
+static struct thread *backtrace_find_crash_thread_from_crash_frame(struct backtrace *backtrace,
+ bool require_abort)
+{
+ /*
+ * This code can be extended to compare something else when the function
+ * name is not available.
+ */
+ if (!backtrace->threads || !backtrace->crash || !backtrace->crash->function)
+ return NULL;
+
+ struct thread *result = NULL;
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ if (thread->frames
+ && thread->frames->function
+ && 0 == strcmp(thread->frames->function, backtrace->crash->function)
+ && (!require_abort || thread_find_abort_frame(thread)))
+ {
+ if (result == NULL)
+ result = thread;
+ else
+ {
+ /* Second frame with the same function. Failure. */
+ return NULL;
+ }
+ }
+
+ thread = thread->next;
+ }
+
+ return result;
+}
+
+struct thread *backtrace_find_crash_thread(struct backtrace *backtrace)
+{
+ /* If there is no thread, be silent and report NULL. */
+ if (!backtrace->threads)
+ return NULL;
+
+ /* If there is just one thread, it is simple. */
+ if (!backtrace->threads->next)
+ return backtrace->threads;
+
+ /* If we have a crash frame *and* there is just one thread which has
+ * this frame on the top, it is also simple.
+ */
+ struct thread *thread;
+ thread = backtrace_find_crash_thread_from_crash_frame(backtrace, false);
+ if (thread)
+ return thread;
+
+ /* There are multiple threads with a frame indistinguishable from
+ * the crash frame on the top of stack.
+ * Try to search for known abort functions.
+ */
+ thread = backtrace_find_crash_thread_from_crash_frame(backtrace, true);
+
+ return thread; /* result or null */
+}
+
+void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth)
+{
+ if (depth <= 0)
+ return;
+
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ struct frame *frame = thread_find_abort_frame(thread);
+ if (frame)
+ frame = frame->next; /* Start counting from the frame following the abort fr. */
+ else
+ frame = thread->frames; /* Start counting from the first frame. */
+
+ /* Skip some frames to get the required stack depth. */
+ int i = depth;
+ struct frame *last_frame = NULL;
+ while (frame && i)
+ {
+ last_frame = frame;
+ frame = frame->next;
+ --i;
+ }
+
+ /* Delete the remaining frames. */
+ if (last_frame)
+ last_frame->next = NULL;
+
+ while (frame)
+ {
+ struct frame *rm = frame;
+ frame = frame->next;
+ frame_free(rm);
+ }
+
+ thread = thread->next;
+ }
+}
+
+void backtrace_remove_exit_handlers(struct backtrace *backtrace)
+{
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_remove_exit_handlers(thread);
+ thread = thread->next;
+ }
+}
+
+void backtrace_remove_noncrash_frames(struct backtrace *backtrace)
+{
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_remove_noncrash_frames(thread);
+ thread = thread->next;
+ }
+}
+
+/* Belongs to independent_backtrace(). */
+struct header
+{
+ struct strbuf *text;
+ struct header *next;
+};
+
+/* Belongs to independent_backtrace(). */
+static struct header *header_new()
+{
+ struct header *head = malloc(sizeof(struct header));
+ if (!head)
+ {
+ puts("Error while allocating memory for backtrace header.");
+ exit(5);
+ }
+ head->text = NULL;
+ head->next = NULL;
+ return head;
+}
+
+/* Recursively frees siblings. */
+/* Belongs to independent_backtrace(). */
+static void header_free(struct header *head)
+{
+ if (head->text)
+ strbuf_free(head->text);
+ if (head->next)
+ header_free(head->next);
+ free(head);
+}
+
+/* Inserts new header to array if it is not already there. */
+/* Belongs to independent_backtrace(). */
+static void header_set_insert(struct header *cur, struct strbuf *new)
+{
+ /* Duplicate found case. */
+ if (strcmp(cur->text->buf, new->buf) == 0)
+ return;
+
+ /* Last item case, insert new header here. */
+ if (cur->next == NULL)
+ {
+ cur->next = header_new();
+ cur->next->text = new;
+ return;
+ }
+
+ /* Move to next item in array case. */
+ header_set_insert(cur->next, new);
+}
+
+struct strbuf *independent_backtrace(const char *input)
+{
+ struct strbuf *header = strbuf_new();
+ bool in_bracket = false;
+ bool in_quote = false;
+ bool in_header = false;
+ bool in_digit = false;
+ bool has_at = false;
+ bool has_filename = false;
+ bool has_bracket = false;
+ struct header *headers = NULL;
+
+ const char *bk = input;
+ while (*bk)
+ {
+ if (bk[0] == '#'
+ && bk[1] >= '0' && bk[1] <= '7'
+ && bk[2] == ' ' /* take only #0...#7 (8 last stack frames) */
+ && !in_quote)
+ {
+ if (in_header && !has_filename)
+ strbuf_clear(header);
+ in_header = true;
+ }
+
+ if (!in_header)
+ {
+ ++bk;
+ continue;
+ }
+
+ if (isdigit(*bk) && !in_quote && !has_at)
+ in_digit = true;
+ else if (bk[0] == '\\' && bk[1] == '\"')
+ bk++;
+ else if (*bk == '\"')
+ in_quote = in_quote == true ? false : true;
+ else if (*bk == '(' && !in_quote)
+ {
+ in_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == ')' && !in_quote)
+ {
+ in_bracket = false;
+ has_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == '\n' && has_filename)
+ {
+ if (headers == NULL)
+ {
+ headers = header_new();
+ headers->text = header;
+ }
+ else
+ header_set_insert(headers, header);
+
+ header = strbuf_new();
+ in_bracket = false;
+ in_quote = false;
+ in_header = false;
+ in_digit = false;
+ has_at = false;
+ has_filename = false;
+ has_bracket = false;
+ }
+ else if (*bk == ',' && !in_quote)
+ in_digit = false;
+ else if (isspace(*bk) && !in_quote)
+ in_digit = false;
+ else if (bk[0] == 'a' && bk[1] == 't' && has_bracket && !in_quote)
+ {
+ has_at = true;
+ strbuf_append_char(header, 'a');
+ }
+ else if (bk[0] == ':' && has_at && isdigit(bk[1]) && !in_quote)
+ has_filename = true;
+ else if (in_header && !in_digit && !in_quote && !in_bracket)
+ strbuf_append_char(header, *bk);
+
+ bk++;
+ }
+
+ strbuf_free(header);
+
+ struct strbuf *result = strbuf_new();
+ struct header *loop = headers;
+ while (loop)
+ {
+ strbuf_append_str(result, loop->text->buf);
+ strbuf_append_char(result, '\n');
+ loop = loop->next;
+ }
+
+ if (headers)
+ header_free(headers); /* recursive */
+
+ return result;
+}
+
+/* Belongs to backtrace_rate_old(). */
+enum line_rating
+{
+ // RATING EXAMPLE
+ MissingEverything = 0, // #0 0x0000dead in ?? ()
+ MissingFunction = 1, // #0 0x0000dead in ?? () from /usr/lib/libfoobar.so.4
+ MissingLibrary = 2, // #0 0x0000dead in foobar()
+ MissingSourceFile = 3, // #0 0x0000dead in FooBar::FooBar () from /usr/lib/libfoobar.so.4
+ Good = 4, // #0 0x0000dead in FooBar::crash (this=0x0) at /home/user/foobar.cpp:204
+ BestRating = Good,
+};
+
+/* Belongs to backtrace_rate_old(). */
+static enum line_rating rate_line(const char *line)
+{
+#define FOUND(x) (strstr(line, x) != NULL)
+ /* see the comments at enum line_rating for possible combinations */
+ if (FOUND(" at "))
+ return Good;
+ const char *function = strstr(line, " in ");
+ if (function && function[4] == '?') /* " in ??" does not count */
+ function = NULL;
+ bool library = FOUND(" from ");
+ if (function && library)
+ return MissingSourceFile;
+ if (function)
+ return MissingLibrary;
+ if (library)
+ return MissingFunction;
+
+ return MissingEverything;
+#undef FOUND
+}
+
+/* just a fallback function, to be removed one day */
+int backtrace_rate_old(const char *backtrace)
+{
+ int i, len;
+ int multiplier = 0;
+ int rating = 0;
+ int best_possible_rating = 0;
+ char last_lvl = 0;
+
+ /* We look at the frames in reversed order, since:
+ * - rate_line() checks starting from the first line of the frame
+ * (note: it may need to look at more than one line!)
+ * - we increase weight (multiplier) for every frame,
+ * so that topmost frames end up most important
+ */
+ len = 0;
+ for (i = strlen(backtrace) - 1; i >= 0; i--)
+ {
+ if (backtrace[i] == '#'
+ && (backtrace[i+1] >= '0' && backtrace[i+1] <= '9') /* #N */
+ && (i == 0 || backtrace[i-1] == '\n')) /* it's at line start */
+ {
+ /* For one, "#0 xxx" always repeats, skip repeats */
+ if (backtrace[i+1] == last_lvl)
+ continue;
+ last_lvl = backtrace[i+1];
+
+ char *s = xstrndup(backtrace + i + 1, len);
+ /* Replace tabs with spaces, rate_line() does not expect tabs.
+ * Actually, even newlines may be there. Example of multiline frame
+ * where " at SRCFILE" is on 2nd line:
+ * #3 0x0040b35d in __libc_message (do_abort=<value optimized out>,
+ * fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:186
+ */
+ char *p;
+ for (p = s; *p; p++)
+ {
+ if (*p == '\t' || *p == '\n')
+ *p = ' ';
+ }
+ int lrate = rate_line(s);
+ multiplier++;
+ rating += lrate * multiplier;
+ best_possible_rating += BestRating * multiplier;
+ //log("lrate:%d rating:%d best_possible_rating:%d s:'%-.40s'",
+ // lrate, rating, best_possible_rating, s);
+ free(s);
+ len = 0; /* starting new line */
+ }
+ else
+ {
+ len++;
+ }
+ }
+
+ /* Bogus 'backtrace' with zero frames? */
+ if (best_possible_rating == 0)
+ return 0;
+
+ /* Returning number of "stars" to show */
+ if (rating*10 >= best_possible_rating*8) /* >= 0.8 */
+ return 4;
+ if (rating*10 >= best_possible_rating*6)
+ return 3;
+ if (rating*10 >= best_possible_rating*4)
+ return 2;
+ if (rating*10 >= best_possible_rating*2)
+ return 1;
+
+ return 0;
+}
+
+float backtrace_quality(struct backtrace *backtrace)
+{
+ int ok_count = 0;
+ int all_count = 0;
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_rating(thread, &ok_count, &all_count);
+ thread = thread->next;
+ }
+
+ if (all_count == 0)
+ return 0;
+ return ok_count / (float)all_count;
+}
diff --git a/lib/utils/backtrace.h b/lib/utils/backtrace.h
new file mode 100644
index 00000000..df5def56
--- /dev/null
+++ b/lib/utils/backtrace.h
@@ -0,0 +1,152 @@
+/*
+ Backtrace parsing and processing.
+
+ If we transform analyzer plugins to separate applications one day,
+ this functionality should be moved to CCpp analyzer, which will
+ then easily provide what abrt-backtrace utility provides now. Currently
+ the code is used by abrt-backtrace, so it is shared in the utils
+ library.
+
+ Copyright (C) 2009, 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 BACKTRACE_H
+#define BACKTRACE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+
+struct frame
+{
+ /* Function name, or NULL. */
+ char *function;
+ /* Frame number. */
+ int number;
+ /* Name of the source file, or binary file, or NULL. */
+ char *sourcefile;
+ bool signal_handler_called;
+ /* Sibling frame, or NULL if this is the last frame in a thread. */
+ struct frame *next;
+};
+
+struct thread
+{
+ int number;
+ struct frame *frames;
+ /* Sibling thread, or NULL if this is the last thread in a backtrace. */
+ struct thread *next;
+};
+
+struct backtrace
+{
+ struct thread *threads;
+ /*
+ * The frame where the crash happened according to GDB.
+ * It might be that we can not tell to which thread this frame belongs,
+ * because all threads end with mutually indistinguishable frames.
+ */
+ struct frame *crash;
+};
+
+extern struct frame *frame_new();
+extern void frame_free(struct frame *f);
+extern struct frame *frame_add_sibling(struct frame *a, struct frame *b);
+
+extern struct thread *thread_new();
+extern void thread_free(struct thread *t);
+extern struct thread *thread_add_sibling(struct thread *a, struct thread *b);
+extern struct frame *thread_find_abort_frame(struct thread *thread);
+
+extern struct backtrace *backtrace_new();
+extern void backtrace_free(struct backtrace *bt);
+
+/* Prints how internal backtrace representation looks to stdout. */
+extern void backtrace_print_tree(struct backtrace *backtrace, bool verbose);
+
+/* Returns the backtrace tree string representation. */
+extern struct strbuf *backtrace_tree_as_str(struct backtrace *backtrace, bool verbose);
+
+/*
+ * Frees all threads except the one provided as parameters.
+ * It does not check whether one is a member of backtrace.
+ * Caller must know that.
+ */
+extern void backtrace_remove_threads_except_one(struct backtrace *backtrace,
+ struct thread *one);
+
+/*
+ * Search all threads and tries to find the one that caused the crash.
+ * It might return NULL if the thread cannot be determined.
+ */
+extern struct thread *backtrace_find_crash_thread(struct backtrace *backtrace);
+
+extern void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth);
+
+/*
+ * Exit handlers are all stack frames above __run_exit_handlers()
+ */
+extern void backtrace_remove_exit_handlers(struct backtrace *backtrace);
+
+/*
+ * Removes frames known as not causing crash, but that are often
+ * a part of a backtrace.
+ */
+extern void backtrace_remove_noncrash_frames(struct backtrace *backtrace);
+
+/* Parses the backtrace and stores it to a structure.
+ * @returns
+ * Returns the backtrace struct representation, or NULL if the parser failed.
+ * Caller of this function is responsible for backtrace_free()ing the returned value.
+ * Defined in backtrace_parser.y.
+ */
+extern struct backtrace *backtrace_parse(char *input, bool debug_parser, bool debug_scanner);
+
+/* Reads the input file and calculates "independent" backtrace from it. "Independent" means
+ * that the memory addresses that differ from run to run are removed from the backtrace, and
+ * also variable names and values are removed.
+ *
+ * This function can be called when backtrace_parse() call fails. It provides a shorter
+ * version of backtrace, with a chance that hash calculated from the returned value can be used
+ * to detect duplicates. However, this kind of duplicate detection is very low-quality.
+ * @returns
+ * The independent backtrace. Caller is responsible for calling
+ * strbuf_free() on it.
+ */
+extern struct strbuf *independent_backtrace(const char *input);
+
+/* Get the quality of backtrace, as a number of "stars".
+ * @returns
+ * Value 0 to 4.
+ */
+extern int backtrace_rate_old(const char *backtrace);
+
+/* Evaluates the quality of the backtrace, meaning the ratio of frames
+ * with function name fully known to all frames.
+ * @returns
+ * A number between 0 and 1. 0 means the lowest quality,
+ * 1 means full backtrace is known.
+ */
+extern float backtrace_quality(struct backtrace *backtrace);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/utils/backtrace_parser.y b/lib/utils/backtrace_parser.y
new file mode 100644
index 00000000..8e2fc2b1
--- /dev/null
+++ b/lib/utils/backtrace_parser.y
@@ -0,0 +1,684 @@
+%{ /* -*- mode: yacc -*-
+ 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 <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "backtrace.h"
+#include "strbuf.h"
+
+struct backtrace *g_backtrace;
+
+#define YYDEBUG 1
+#define YYMAXDEPTH 10000000
+void yyerror(char const *s)
+{
+ fprintf (stderr, "\nParser error: %s\n", s);
+}
+
+int yylex();
+
+%}
+
+/* This defines the type of yylval */
+%union {
+ struct backtrace *backtrace;
+ struct thread *thread;
+ struct frame *frame;
+ char *str;
+ int num;
+ char c;
+
+ struct strbuf *strbuf;
+}
+
+/* Bison declarations. */
+%token END 0 "end of file"
+
+%type <backtrace> backtrace
+%type <thread> threads
+ thread
+%type <frame> frames
+ frame
+ frame_head
+ frame_head_1
+ frame_head_2
+ frame_head_3
+ frame_head_4
+ frame_head_5
+%type <strbuf> identifier
+ hexadecimal_digit_sequence
+ hexadecimal_number
+ file_name
+ file_location
+ function_call
+ function_name
+ digit_sequence
+ frame_address_in_function
+ identifier_braces
+ identifier_braces_inside
+ identifier_template
+ identifier_template_inside
+%type <c> nondigit
+ digit
+ hexadecimal_digit
+ file_name_char
+ identifier_char
+ identifier_char_no_templates
+ identifier_first_char
+ identifier_braces_inside_char
+ identifier_template_inside_char
+ variables_char
+ variables_char_no_framestart
+ ws
+ ws_nonl
+ '(' ')' '+' '-' '/' '.' '_' '~' '[' ']' '\r' '?' '{' '}'
+ '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'
+ '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' '
+ '\n' '\t' '\\' '!' '*' '%' '|' '^' '&' '$'
+%type <num> frame_start
+
+%destructor { thread_free($$); } <thread>
+%destructor { frame_free($$); } <frame>
+%destructor { strbuf_free($$); } <strbuf>
+
+%start backtrace
+%glr-parser
+%error-verbose
+%locations
+
+%% /* The grammar follows. */
+
+backtrace : /* empty */ %dprec 1
+ { $$ = g_backtrace = backtrace_new(); }
+ | threads wsa %dprec 2
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $1;
+ }
+ | frame_head wss threads wsa %dprec 4
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $3;
+ $$->crash = $1;
+ }
+ | frame wss threads wsa %dprec 3
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $3;
+ $$->crash = $1;
+ }
+;
+
+threads : thread
+ | threads '\n' thread { $$ = thread_add_sibling($1, $3); }
+;
+
+thread : keyword_thread wss digit_sequence wsa '(' keyword_thread wss digit_sequence wsa ')' ':' wsa frames
+ {
+ $$ = thread_new();
+ $$->frames = $13;
+
+ if (sscanf($3->buf, "%d", &$$->number) != 1)
+ {
+ printf("Error while parsing thread number '%s'", $3->buf);
+ exit(5);
+ }
+ strbuf_free($3);
+ strbuf_free($8);
+ }
+;
+
+frames : frame { $$ = $1; }
+ | frames frame { $$ = frame_add_sibling($1, $2); }
+;
+
+frame : frame_head_1 wss variables %dprec 3
+ | frame_head_2 wss variables %dprec 4
+ | frame_head_3 wss variables %dprec 5
+ | frame_head_4 wss variables %dprec 2
+ | frame_head_5 wss variables %dprec 1
+;
+
+frame_head : frame_head_1 %dprec 3
+ | frame_head_2 %dprec 4
+ | frame_head_3 %dprec 5
+ | frame_head_4 %dprec 2
+ | frame_head_5 %dprec 1
+;
+
+frame_head_1 : frame_start wss function_call wsa keyword_at wss file_location
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
+ $$->sourcefile = $7->buf;
+ strbuf_free_nobuf($7);
+ }
+;
+
+frame_head_2 : frame_start wss frame_address_in_function wss keyword_at wss file_location
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
+ $$->sourcefile = $7->buf;
+ strbuf_free_nobuf($7);
+ }
+;
+
+frame_head_3 : frame_start wss frame_address_in_function wss keyword_from wss file_location
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
+ $$->sourcefile = $7->buf;
+ strbuf_free_nobuf($7);
+ }
+;
+
+frame_head_4 : frame_start wss frame_address_in_function
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
+ }
+;
+
+frame_head_5 : frame_start wss keyword_sighandler
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->signal_handler_called = true;
+ }
+
+frame_start: '#' digit_sequence
+ {
+ if (sscanf($2->buf, "%d", &$$) != 1)
+ {
+ printf("Error while parsing frame number '%s'.\n", $2->buf);
+ exit(5);
+ }
+ strbuf_free($2);
+ }
+;
+
+frame_address_in_function : hexadecimal_number wss keyword_in wss function_call
+ {
+ strbuf_free($1);
+ $$ = $5;
+ }
+ | hexadecimal_number wss keyword_in wss keyword_vtable wss keyword_for wss function_call
+ {
+ strbuf_free($1);
+ $$ = $9;
+ }
+;
+
+file_location : file_name ':' digit_sequence
+ {
+ $$ = $1;
+ strbuf_free($3); /* line number not needed for now */
+ }
+ | file_name
+;
+
+variables : variables_line '\n'
+ | variables_line END
+ | variables_line wss_nonl '\n'
+ | variables_line wss_nonl END
+ | variables variables_line '\n'
+ | variables variables_line END
+ | variables variables_line wss_nonl '\n'
+ | variables variables_line wss_nonl END
+ | variables wss_nonl variables_line '\n'
+ | variables wss_nonl variables_line END
+ | variables wss_nonl variables_line wss_nonl '\n'
+ | variables wss_nonl variables_line wss_nonl END
+;
+
+variables_line : variables_char_no_framestart
+ | variables_line variables_char
+ | variables_line wss_nonl variables_char
+;
+
+variables_char : '#' | variables_char_no_framestart
+;
+
+/* Manually synchronized with function_args_char_base, except the first line. */
+variables_char_no_framestart : digit | nondigit | '"' | '(' | ')' | '\\'
+ | '+' | '-' | '<' | '>' | '/' | '.'
+ | '[' | ']' | '?' | '\'' | '`' | ','
+ | '=' | '{' | '}' | '^' | '&' | '$'
+ | ':' | ';' | '!' | '@' | '*'
+ | '%' | '|' | '~'
+;
+
+function_call : function_name wss function_args %dprec 3
+ | return_type wss_nonl function_name wss function_args %dprec 2
+ { $$ = $3; }
+ | function_name wss_nonl identifier_template wss function_args %dprec 1
+ { $$ = $1; strbuf_free($3); }
+;
+
+return_type : identifier { strbuf_free($1); }
+;
+
+function_name : identifier
+ | '?' '?'
+ {
+ $$ = strbuf_new();
+ strbuf_append_str($$, "??");
+ }
+;
+
+function_args : '(' wsa ')'
+ | '(' wsa function_args_sequence wsa ')'
+;
+
+function_args_sequence : function_args_char
+ | function_args_sequence wsa '(' wsa ')'
+ | function_args_sequence wsa '(' wsa function_args_string wsa ')'
+ | function_args_sequence wsa '(' wsa function_args_sequence wsa ')'
+ | function_args_sequence wsa function_args_char
+ | function_args_sequence wsa function_args_string
+;
+
+function_args_string : '"' wsa function_args_string_sequence wsa '"'
+ | '"' wsa '"'
+;
+
+/* Manually synchronized with variables_char_no_framestart,
+ * except the first line.
+ */
+function_args_char_base : digit | nondigit | '#'
+ | '+' | '-' | '<' | '>' | '/' | '.'
+ | '[' | ']' | '?' | '\'' | '`' | ','
+ | '=' | '{' | '}' | '^' | '&' | '$'
+ | ':' | ';' | '!' | '@' | '*'
+ | '%' | '|' | '~'
+;
+function_args_escaped_char : '\\' function_args_char_base
+ | '\\' '\\'
+ | '\\' '"'
+;
+function_args_char : function_args_char_base
+ | function_args_escaped_char
+;
+
+
+function_args_string_sequence : function_args_string_char
+ | function_args_string_sequence function_args_string_char
+ | function_args_string_sequence wss_nonl function_args_string_char
+;
+
+function_args_string_char : function_args_char | '(' | ')'
+;
+
+file_name : file_name_char { $$ = strbuf_new(); strbuf_append_char($$, $1); }
+ | file_name file_name_char { $$ = strbuf_append_char($1, $2); }
+;
+
+file_name_char : digit | nondigit | '-' | '+' | '/' | '.'
+;
+
+ /* Function name, sometimes mangled.
+ * Example: something@GLIB_2_2
+ * CClass::operator=
+ */
+identifier : identifier_first_char %dprec 1
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ }
+ | identifier_braces %dprec 1 /* e.g. (anonymous namespace)::WorkerThread */
+ | identifier identifier_char %dprec 1
+ { $$ = strbuf_append_char($1, $2); }
+ | identifier identifier_braces %dprec 1
+ {
+ $$ = strbuf_append_str($1, $2->buf);
+ strbuf_free($2);
+ }
+ | identifier identifier_template %dprec 2
+ {
+ $$ = strbuf_append_str($1, $2->buf);
+ strbuf_free($2);
+ }
+;
+
+identifier_first_char: nondigit
+ | '~' /* destructor */
+ | '*'
+;
+
+identifier_char_no_templates : digit | nondigit | '@' | '.' | ':' | '='
+ | '!' | '*' | '+' | '-' | '[' | ']'
+ | '~' | '&' | '/' | '%' | '^'
+ | '|' | ','
+;
+
+/* Most of the special characters are required to support C++
+ * operator overloading.
+ */
+identifier_char : identifier_char_no_templates | '<'| '>'
+;
+
+identifier_braces : '(' ')'
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ strbuf_append_char($$, $2);
+ }
+ | '(' identifier_braces_inside ')'
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ strbuf_append_str($$, $2->buf);
+ strbuf_free($2);
+ strbuf_append_char($$, $3);
+ }
+;
+
+identifier_braces_inside : identifier_braces_inside_char %dprec 1
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ }
+ | identifier_braces_inside identifier_braces_inside_char %dprec 1
+ { $$ = strbuf_append_char($1, $2); }
+ | identifier_braces_inside '(' identifier_braces_inside ')' %dprec 1
+ {
+ $$ = strbuf_append_char($1, $2);
+ $$ = strbuf_append_str($1, $3->buf);
+ strbuf_free($3);
+ $$ = strbuf_append_char($1, $4);
+ }
+ | identifier_braces_inside '(' ')' %dprec 1
+ {
+ $$ = strbuf_append_char($1, $2);
+ $$ = strbuf_append_char($1, $3);
+ }
+ | identifier_braces_inside identifier_template %dprec 2
+ {
+ $$ = strbuf_append_str($1, $2->buf);
+ strbuf_free($2);
+ }
+;
+
+identifier_braces_inside_char : identifier_char | ws_nonl
+;
+
+identifier_template : '<' identifier_template_inside '>'
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ strbuf_append_str($$, $2->buf);
+ strbuf_free($2);
+ strbuf_append_char($$, $3);
+ }
+;
+
+identifier_template_inside : identifier_template_inside_char
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ }
+ | identifier_template_inside identifier_template_inside_char
+ { $$ = strbuf_append_char($1, $2); }
+ | identifier_template_inside '<' identifier_template_inside '>'
+ {
+ $$ = strbuf_append_char($1, $2);
+ $$ = strbuf_append_str($1, $3->buf);
+ strbuf_free($3);
+ $$ = strbuf_append_char($1, $4);
+ }
+ | identifier_template_inside identifier_braces
+ {
+ $$ = strbuf_append_str($1, $2->buf);
+ strbuf_free($2);
+ }
+;
+
+identifier_template_inside_char : identifier_char_no_templates | ws_nonl
+;
+
+digit_sequence : digit { $$ = strbuf_new(); strbuf_append_char($$, $1); }
+ | digit_sequence digit { $$ = strbuf_append_char($1, $2); }
+;
+
+hexadecimal_number : '0' 'x' hexadecimal_digit_sequence
+ {
+ $$ = $3;
+ strbuf_prepend_str($$, "0x");
+ }
+ | '0' 'X' hexadecimal_digit_sequence
+ {
+ $$ = $3;
+ strbuf_prepend_str($$, "0X");
+ }
+;
+
+hexadecimal_digit_sequence : hexadecimal_digit
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ }
+ | hexadecimal_digit_sequence hexadecimal_digit
+ { $$ = strbuf_append_char($1, $2); }
+;
+
+hexadecimal_digit : digit
+ | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
+ | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
+;
+
+digit : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
+;
+
+nondigit : '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'
+ | '_'
+;
+
+ /* whitespace */
+ws : ws_nonl | '\n' | '\r'
+;
+
+ /* No newline.*/
+ws_nonl : '\t' | ' '
+;
+
+ /* whitespace sequence without a newline */
+wss_nonl : ws_nonl
+ | wss_nonl ws_nonl
+;
+
+ /* whitespace sequence */
+wss : ws
+ | wss ws
+;
+
+ /* whitespace sequence allowed */
+wsa :
+ | wss
+;
+
+keyword_in : 'i' 'n'
+;
+
+keyword_at : 'a' 't'
+;
+
+keyword_for : 'f' 'o' 'r'
+;
+
+keyword_vtable : 'v' 't' 'a' 'b' 'l' 'e'
+;
+
+keyword_from : 'f' 'r' 'o' 'm'
+;
+
+keyword_thread: 'T' 'h' 'r' 'e' 'a' 'd'
+;
+
+keyword_sighandler: '<' 's' 'i' 'g' 'n' 'a' 'l' ' ' 'h' 'a' 'n' 'd' 'l' 'e' 'r' ' ' 'c' 'a' 'l' 'l' 'e' 'd' '>'
+;
+
+%%
+
+static bool scanner_echo = false;
+static char *yyin;
+
+int yylex()
+{
+ char c = *yyin;
+ if (c == '\0')
+ return END;
+ ++yyin;
+
+ /* Debug output. */
+ if (scanner_echo)
+ putchar(c);
+
+ yylval.c = c;
+
+ /* Return a single char. */
+ return c;
+}
+
+/* This is the function that is actually called from outside.
+ * @returns
+ * Backtrace structure. Caller is responsible for calling
+ * backtrace_free() on this.
+ * Returns NULL when parsing failed.
+ */
+struct backtrace *backtrace_parse(char *input, bool debug_parser, bool debug_scanner)
+{
+ /* Skip the backtrace header information. */
+ char *btnoheader_a = strstr(input, "\nThread ");
+ char *btnoheader_b = strstr(input, "\n#");
+ char *btnoheader = input;
+ if (btnoheader_a)
+ {
+ if (btnoheader_b && btnoheader_b < btnoheader_a)
+ btnoheader = btnoheader_b + 1;
+ else
+ btnoheader = btnoheader_a + 1;
+ }
+ else if (btnoheader_b)
+ btnoheader = btnoheader_b + 1;
+
+ /* Bug fixing hack for broken backtraces.
+ * Sometimes the empty line is missing before new Thread section.
+ * This is against rules, but a bug (now fixed) in Linux kernel caused
+ * this.
+ */
+ char *thread_fixer = btnoheader + 1;
+ while ((thread_fixer = strstr(thread_fixer, "\nThread")) != NULL)
+ {
+ if (thread_fixer[-1] != '\n')
+ thread_fixer[-1] = '\n';
+
+ ++thread_fixer;
+ }
+
+ /* Bug fixing hack for GDB - remove wrongly placed newlines from the backtrace.
+ * Sometimes there is a newline in the local variable section.
+ * This is caused by some GDB hooks.
+ * Example: rhbz#538440
+ * #1 0x0000000000420939 in sync_deletions (mse=0x0, mfld=0x1b85020)
+ * at mail-stub-exchange.c:1119
+ * status = <value optimized out>
+ * iter = 0x1af38d0
+ * known_messages = 0x1b5c460Traceback (most recent call last):
+ * File "/usr/share/glib-2.0/gdb/glib.py", line 98, in next
+ * if long (node["key_hash"]) >= 2:
+ * RuntimeError: Cannot access memory at address 0x11
+ *
+ * __PRETTY_FUNCTION__ = "sync_deletions"
+ * #2 0x0000000000423e6b in refresh_folder (stub=0x1b77f10 [MailStubExchange],
+ * ...
+ *
+ * The code removes every empty line (also those containing only spaces),
+ * which is not followed by a new Thread section.
+ *
+ * rhbz#555251 contains empty lines with spaces
+ */
+ char *empty_line = btnoheader;
+ char *c = btnoheader;
+ while (*c)
+ {
+ if (*c == '\n')
+ {
+ char *cend = c + 1;
+ while (*cend == ' ' || *cend == '\t')
+ ++cend;
+ if (*cend == '\n' && 0 != strncmp(cend, "\nThread", strlen("\nThread")))
+ memmove(c, cend, strlen(cend) + 1);
+ }
+ ++c;
+ }
+ while ((empty_line = strstr(empty_line, "\n\n")) != NULL)
+ {
+ if (0 != strncmp(empty_line, "\n\nThread", strlen("\n\nThread")))
+ {
+ /* Remove the empty line by converting the first newline to char. */
+ empty_line[0] = 'X';
+ }
+ ++empty_line;
+ }
+
+ /* Prepare for running parser. */
+ g_backtrace = 0;
+ yyin = btnoheader;
+#if YYDEBUG == 1
+ if (debug_parser)
+ yydebug = 1;
+#endif
+ scanner_echo = debug_scanner;
+
+ /* Parse. */
+ int failure = yyparse();
+
+ /* Separate debugging output. */
+ if (scanner_echo)
+ putchar('\n');
+
+ if (failure)
+ {
+ if (g_backtrace)
+ {
+ backtrace_free(g_backtrace);
+ g_backtrace = NULL;
+ }
+ fprintf(stderr, "Error while parsing backtrace.\n");
+ }
+
+ return g_backtrace;
+}
diff --git a/lib/utils/copyfd.cpp b/lib/utils/copyfd.cpp
new file mode 100644
index 00000000..cbcf7005
--- /dev/null
+++ b/lib/utils/copyfd.cpp
@@ -0,0 +1,171 @@
+/*
+ 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 = 0)
+{
+ 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);
+ 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);
+ close(src);
+ close(dst);
+ return r;
+}
diff --git a/lib/utils/daemon.cpp b/lib/utils/daemon.cpp
new file mode 100644
index 00000000..944aef0c
--- /dev/null
+++ b/lib/utils/daemon.cpp
@@ -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/lib/utils/dirsize.cpp b/lib/utils/dirsize.cpp
new file mode 100644
index 00000000..739b6b73
--- /dev/null
+++ b/lib/utils/dirsize.cpp
@@ -0,0 +1,100 @@
+/*
+ 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"
+
+using namespace std;
+
+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;
+ string dname = concat_path_file(pPath, ep->d_name);
+ if (lstat(dname.c_str(), &statbuf) != 0)
+ continue;
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ size += get_dirsize(dname.c_str());
+ }
+ else if (S_ISREG(statbuf.st_mode))
+ {
+ size += statbuf.st_size;
+ }
+ }
+ closedir(dp);
+ return size;
+}
+
+double get_dirsize_find_largest_dir(
+ const char *pPath,
+ string *worst_dir,
+ const char *excluded)
+{
+ 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;
+ string dname = concat_path_file(pPath, ep->d_name);
+ if (lstat(dname.c_str(), &statbuf) != 0)
+ continue;
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ double sz = get_dirsize(dname.c_str());
+ 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;
+ *worst_dir = ep->d_name;
+ }
+ }
+ }
+ else if (S_ISREG(statbuf.st_mode))
+ {
+ size += statbuf.st_size;
+ }
+ }
+ closedir(dp);
+ return size;
+}
diff --git a/lib/utils/encbase64.cpp b/lib/utils/encbase64.cpp
new file mode 100644
index 00000000..6a6f1f75
--- /dev/null
+++ b/lib/utils/encbase64.cpp
@@ -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/lib/utils/hooklib.cpp b/lib/utils/hooklib.cpp
new file mode 100644
index 00000000..68970661
--- /dev/null
+++ b/lib/utils/hooklib.cpp
@@ -0,0 +1,133 @@
+/*
+ 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 "debug_dump.h"
+#include <sys/statvfs.h>
+
+using namespace std;
+
+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;
+
+ char line[256];
+ while (1)
+ {
+ if (fgets(line, sizeof(line), fp) == NULL)
+ {
+ fclose(fp);
+ if (additional_conf)
+ {
+ /* Next .conf file plz */
+ fp = fopen(additional_conf, "r");
+ if (fp)
+ {
+ additional_conf = NULL;
+ continue;
+ }
+ }
+ break;
+ }
+
+ strchrnul(line, '\n')[0] = '\0';
+ 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 != '=')
+ continue;
+ 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;
+ }
+ continue;
+ }
+#undef DIRECTIVE
+#define DIRECTIVE "MakeCompatCore"
+ if (setting_MakeCompatCore && strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)
+ {
+ p = skip_whitespace(p + sizeof(DIRECTIVE)-1);
+ if (*p != '=')
+ continue;
+ p = skip_whitespace(p + 1);
+ *setting_MakeCompatCore = string_to_bool(p);
+ continue;
+ }
+#undef DIRECTIVE
+#define DIRECTIVE "SaveBinaryImage"
+ if (setting_SaveBinaryImage && strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)
+ {
+ p = skip_whitespace(p + sizeof(DIRECTIVE)-1);
+ if (*p != '=')
+ continue;
+ p = skip_whitespace(p + 1);
+ *setting_SaveBinaryImage = string_to_bool(p);
+ continue;
+ }
+#undef DIRECTIVE
+ /* add more 'if (strncmp(p, DIRECTIVE, sizeof(DIRECTIVE)-1) == 0)' here... */
+ }
+}
+
+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;
+ string worst_dir;
+ while (--count >= 0)
+ {
+ const char *base_dirname = strrchr(exclude_path, '/') + 1; /* never NULL */
+ /* We exclude our own dump from candidates for deletion (3rd param): */
+ double dirsize = get_dirsize_find_largest_dir(DEBUG_DUMPS_DIR, &worst_dir, base_dirname);
+ if (dirsize / (1024*1024) < setting_MaxCrashReportsSize || worst_dir == "")
+ break;
+ log("size of '%s' >= %u MB, deleting '%s'", DEBUG_DUMPS_DIR, setting_MaxCrashReportsSize, worst_dir.c_str());
+ delete_debug_dump_dir(concat_path_file(DEBUG_DUMPS_DIR, worst_dir.c_str()).c_str());
+ worst_dir = "";
+ }
+}
diff --git a/lib/utils/hooklib.h b/lib/utils/hooklib.h
new file mode 100644
index 00000000..1651204f
--- /dev/null
+++ b/lib/utils/hooklib.h
@@ -0,0 +1,21 @@
+/*
+ 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.
+*/
+
+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);
diff --git a/lib/utils/logging.c b/lib/utils/logging.c
new file mode 100644
index 00000000..a6eaa50f
--- /dev/null
+++ b/lib/utils/logging.c
@@ -0,0 +1,143 @@
+/*
+ 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 "logging.h"
+
+int xfunc_error_retval = EXIT_FAILURE;
+int g_verbose;
+int logmode = LOGMODE_STDIO;
+const char *msg_prefix = "";
+const char *msg_eol = "\n";
+void (*g_custom_logger)(const char*);
+
+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 = strlen(msg_prefix);
+ 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) {
+ memmove(msg + prefix_len, msg, used);
+ used += prefix_len;
+ memcpy(msg, msg_prefix, prefix_len);
+ }
+ 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_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 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 die_out_of_memory(void)
+{
+ error_msg_and_die("Out of memory, exiting");
+}
diff --git a/lib/utils/logging.h b/lib/utils/logging.h
new file mode 100644
index 00000000..f682feb0
--- /dev/null
+++ b/lib/utils/logging.h
@@ -0,0 +1,83 @@
+/*
+ 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>
+
+#include "read_write.h"
+#include "xfuncs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NORETURN __attribute__ ((noreturn))
+
+
+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;
+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 perror_nomsg_and_die(void) NORETURN;
+void perror_nomsg(void);
+void verror_msg(const char *s, va_list p, const char *strerr);
+void die_out_of_memory(void) NORETURN;
+
+/* Verbosity level */
+extern int g_verbose;
+/* VERB1 log("what you sometimes want to see, even on a production box") */
+#define VERB1 if (g_verbose >= 1)
+/* VERB2 log("debug message, not going into insanely small details") */
+#define VERB2 if (g_verbose >= 2)
+/* VERB3 log("lots and lots of details") */
+#define VERB3 if (g_verbose >= 3)
+/* there is no level > 3 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/utils/make_descr.cpp b/lib/utils/make_descr.cpp
new file mode 100644
index 00000000..46d9644d
--- /dev/null
+++ b/lib/utils/make_descr.cpp
@@ -0,0 +1,236 @@
+/*
+ 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"
+#include "debug_dump.h" /* FILENAME_ARCHITECTURE etc */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(S) gettext(S)
+#else
+# define _(S) (S)
+#endif
+
+using namespace std;
+
+static void add_content(bool &was_multiline, string& description, const char *header, const char *content)
+{
+ /* We separate multiline contents with emply line */
+ if (was_multiline)
+ description += '\n';
+
+ while (content[0] == '\n')
+ content++;
+
+ if (strchr(content, '\n') == NULL)
+ {
+ if (skip_whitespace(content)[0] == '\0')
+ {
+ /* empty, dont report at all */
+ return;
+ }
+ /* one string value, like OS release */
+ description += header;
+ description += ": ";
+ description += content;
+ description += '\n';
+ was_multiline = 0;
+ }
+ else
+ {
+ /* multi-string value, like backtrace */
+ if (!was_multiline && description.size() != 0) /* if wasn't yet separated */
+ description += '\n'; /* do it now */
+ description += header;
+ description += "\n-----\n";
+ description += content;
+ if (content[strlen(content) - 1] != '\n')
+ description += '\n';
+ was_multiline = 1;
+ }
+}
+
+/* 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_GLOBAL_UUID,
+ CD_UUID ,
+ CD_INFORMALL ,
+ CD_DUPHASH ,
+ CD_DUMPDIR ,
+ CD_COUNT ,
+ CD_REPORTED ,
+ CD_MESSAGE ,
+ NULL
+};
+
+string make_description_bz(const map_crash_data_t& pCrashData)
+{
+ string description;
+ string long_description;
+
+ map_crash_data_t::const_iterator it = pCrashData.begin();
+ for (; it != pCrashData.end(); it++)
+ {
+ const string& itemname = it->first;
+ const string& type = it->second[CD_TYPE];
+ const string& content = it->second[CD_CONTENT];
+ if (type == CD_TXT)
+ {
+ /* Skip items we are not interested in */
+ const char *const *bl = blacklisted_items;
+ while (*bl)
+ {
+ if (itemname == *bl)
+ break;
+ bl++;
+ }
+ if (*bl)
+ continue; /* blacklisted */
+ if (content == "1.\n2.\n3.\n")
+ continue; /* user did not change default "How to reproduce" */
+
+ if (content.size() <= CD_TEXT_ATT_SIZE)
+ {
+ /* Add small (less than few kb) text items inline */
+ bool was_multiline = 0;
+ string tmp;
+ add_content(was_multiline,
+ tmp,
+ /* "reproduce: blah" looks ugly, fixing: */
+ itemname == FILENAME_REPRODUCE ? "How to reproduce" : itemname.c_str(),
+ content.c_str()
+ );
+
+ if (was_multiline)
+ {
+ /* Not one-liner */
+ if (long_description.size() != 0)
+ long_description += '\n';
+ long_description += tmp;
+ }
+ else
+ {
+ description += tmp;
+ }
+ } else {
+ bool was_multiline = 0;
+ add_content(was_multiline, description, "Attached file", itemname.c_str());
+ }
+ }
+ }
+
+ /* One-liners go first, then multi-line items */
+ if (description.size() != 0 && long_description.size() != 0)
+ {
+ description += '\n';
+ }
+ description += long_description;
+
+ return description;
+}
+
+string make_description_logger(const map_crash_data_t& pCrashData)
+{
+ string description;
+ string long_description;
+
+ map_crash_data_t::const_iterator it = pCrashData.begin();
+ for (; it != pCrashData.end(); it++)
+ {
+ const string &filename = it->first;
+ const string &type = it->second[CD_TYPE];
+ const string &content = it->second[CD_CONTENT];
+ if (type == CD_TXT
+ || type == CD_BIN
+ ) {
+ /* 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 (content == "1.\n2.\n3.\n")
+ continue; /* user did not change default "How to reproduce" */
+
+ bool was_multiline = 0;
+ string tmp;
+ add_content(was_multiline, tmp, filename.c_str(), content.c_str());
+
+ if (was_multiline)
+ {
+ if (long_description.size() != 0)
+ long_description += '\n';
+ long_description += tmp;
+ }
+ else
+ {
+ description += tmp;
+ }
+ }
+ }
+
+ if (description.size() != 0 && long_description.size() != 0)
+ {
+ description += '\n';
+ }
+ description += long_description;
+
+ return description;
+}
+
+string make_description_reproduce_comment(const map_crash_data_t& pCrashData)
+{
+ map_crash_data_t::const_iterator end = pCrashData.end();
+ map_crash_data_t::const_iterator it;
+
+ string howToReproduce;
+ it = pCrashData.find(FILENAME_REPRODUCE);
+ if (it != end)
+ {
+ if ((it->second[CD_CONTENT].size() > 0)
+ && (it->second[CD_CONTENT] != "1.\n2.\n3.\n"))
+ {
+ howToReproduce = "\n\nHow to reproduce\n"
+ "-----\n";
+ howToReproduce += it->second[CD_CONTENT];
+ }
+ }
+ string comment;
+ it = pCrashData.find(FILENAME_COMMENT);
+ if (it != end)
+ {
+ if (it->second[CD_CONTENT].size() > 0)
+ {
+ comment = "\n\nComment\n"
+ "-----\n";
+ comment += it->second[CD_CONTENT];
+ }
+ }
+ return howToReproduce + comment;
+}
diff --git a/lib/utils/numtoa.cpp b/lib/utils/numtoa.cpp
new file mode 100644
index 00000000..061da553
--- /dev/null
+++ b/lib/utils/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/lib/utils/parse_release.cpp b/lib/utils/parse_release.cpp
new file mode 100644
index 00000000..3d6b572f
--- /dev/null
+++ b/lib/utils/parse_release.cpp
@@ -0,0 +1,64 @@
+/*
+ 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"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "strbuf.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/lib/utils/read_write.c b/lib/utils/read_write.c
new file mode 100644
index 00000000..3f2133e6
--- /dev/null
+++ b/lib/utils/read_write.c
@@ -0,0 +1,94 @@
+/*
+ * Utility routines.
+ *
+ * Licensed under GPLv2 or later, see file COPYING in this tarball for details.
+ */
+
+#include "read_write.h"
+
+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 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;
+}
+
+/* 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_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_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;
+}
diff --git a/lib/utils/read_write.h b/lib/utils/read_write.h
new file mode 100644
index 00000000..5a351869
--- /dev/null
+++ b/lib/utils/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>
+
+#include "logging.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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/utils/skip_whitespace.cpp b/lib/utils/skip_whitespace.cpp
new file mode 100644
index 00000000..816928bf
--- /dev/null
+++ b/lib/utils/skip_whitespace.cpp
@@ -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/lib/utils/spawn.cpp b/lib/utils/spawn.cpp
new file mode 100644
index 00000000..dace7d4b
--- /dev/null
+++ b/lib/utils/spawn.cpp
@@ -0,0 +1,142 @@
+/*
+ * Utility routines.
+ *
+ * Licensed under GPLv2, see file COPYING in this tarball for details.
+ */
+#include "abrtlib.h"
+
+using namespace std;
+
+static string concat_str_vector(char **strings)
+{
+ string result;
+ while (*strings) {
+ result += *strings++;
+ if (*strings)
+ result += ' ';
+ }
+ 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 log("Executing: %s", concat_str_vector(argv).c_str());
+
+ 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/lib/utils/strbuf.c b/lib/utils/strbuf.c
new file mode 100644
index 00000000..9ad74714
--- /dev/null
+++ b/lib/utils/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 "strbuf.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include "xfuncs.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 = xmalloc(sizeof(struct strbuf));
+ buf->alloc = 8;
+ buf->len = 0;
+ buf->buf = xmalloc(buf->alloc);
+ buf->buf[buf->len] = '\0';
+ return buf;
+}
+
+void strbuf_free(struct strbuf *strbuf)
+{
+ 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 num characters
+ * without touching malloc/realloc.
+ */
+static void strbuf_grow(struct strbuf *strbuf, int num)
+{
+ if (strbuf->len + num + 1 > strbuf->alloc)
+ {
+ while (strbuf->len + num + 1 > strbuf->alloc)
+ strbuf->alloc *= 2; /* huge grow = infinite loop */
+
+ strbuf->buf = xrealloc(strbuf->buf, strbuf->alloc);
+ }
+}
+
+struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c)
+{
+ strbuf_grow(strbuf, 1);
+ strbuf->buf[strbuf->len++] = c;
+ strbuf->buf[strbuf->len] = '\0';
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(strbuf, len);
+ assert(strbuf->len + len < strbuf->alloc);
+ strcpy(strbuf->buf + strbuf->len, str);
+ strbuf->len += len;
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(strbuf, len);
+ assert(strbuf->len + len < strbuf->alloc);
+ memmove(strbuf->buf + len, strbuf->buf, strbuf->len + 1);
+ memcpy(strbuf->buf, str, len);
+ strbuf->len += 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/lib/utils/strbuf.h b/lib/utils/strbuf.h
new file mode 100644
index 00000000..3f3ba51a
--- /dev/null
+++ b/lib/utils/strbuf.h
@@ -0,0 +1,51 @@
+/*
+ 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 message, without the ending \0. */
+ int len;
+ char *buf;
+};
+
+extern struct strbuf *strbuf_new();
+extern void strbuf_free(struct strbuf *strbuf);
+/* Releases strbuf, but not the internal buffer. */
+extern char* strbuf_free_nobuf(struct strbuf *strbuf);
+extern void strbuf_clear(struct strbuf *strbuf);
+extern struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c);
+extern struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str);
+extern struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str);
+extern struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...);
+extern struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/utils/stringops.cpp b/lib/utils/stringops.cpp
new file mode 100644
index 00000000..7bc5413f
--- /dev/null
+++ b/lib/utils/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/lib/utils/test.cpp b/lib/utils/test.cpp
new file mode 100644
index 00000000..35edb0c5
--- /dev/null
+++ b/lib/utils/test.cpp
@@ -0,0 +1,107 @@
+/*
+ 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("RunApp");
+ middleWare.RegisterPlugin("SQLite3");
+ middleWare.SetDatabase("SQLite3");
+ middleWare.SetOpenGPGCheck(false);
+ middleWare.AddActionOrReporter("Logger", "");
+ middleWare.AddAnalyzerActionOrReporter("CCpp", "Mailx", "");
+ middleWare.AddAnalyzerActionOrReporter("CCpp", "RunApp", "date");
+
+ 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[CD_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/lib/utils/time.cpp b/lib/utils/time.cpp
new file mode 100644
index 00000000..37ade2cc
--- /dev/null
+++ b/lib/utils/time.cpp
@@ -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/lib/utils/xatonum.cpp b/lib/utils/xatonum.cpp
new file mode 100644
index 00000000..83146298
--- /dev/null
+++ b/lib/utils/xatonum.cpp
@@ -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/lib/utils/xconnect.cpp b/lib/utils/xconnect.cpp
new file mode 100644
index 00000000..0d02b1a4
--- /dev/null
+++ b/lib/utils/xconnect.cpp
@@ -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/lib/utils/xfuncs.c b/lib/utils/xfuncs.c
new file mode 100644
index 00000000..bd6efec8
--- /dev/null
+++ b/lib/utils/xfuncs.c
@@ -0,0 +1,391 @@
+/*
+ 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 "xfuncs.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);
+}
+
+// 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);
+}
+
+const char *get_home_dir(uid_t uid)
+{
+ struct passwd* pw = getpwuid(uid);
+ // TODO: handle errno
+ return pw ? pw->pw_dir : NULL;
+}
+
+// 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);
+}
+
+#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
+
+void xunlink(const char *pathname)
+{
+ if (unlink(pathname))
+ perror_msg_and_die("can't remove file '%s'", pathname);
+}
+
+/* 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);
+}