summaryrefslogtreecommitdiffstats
path: root/lib/utils/DebugDump.cpp
diff options
context:
space:
mode:
authorNikola Pajkovsky <npajkovs@redhat.com>2010-08-10 10:21:25 +0200
committerNikola Pajkovsky <npajkovs@redhat.com>2010-08-10 10:21:56 +0200
commit83a6ce9ad4b1828e163dc7172ef603201b748473 (patch)
tree9d0580eba6c01cb5964655df42bafab9de91329b /lib/utils/DebugDump.cpp
parente84ab7783d05eb7b5f1b55ab44e7c23c85e50516 (diff)
downloadabrt-83a6ce9ad4b1828e163dc7172ef603201b748473.tar.gz
abrt-83a6ce9ad4b1828e163dc7172ef603201b748473.tar.xz
abrt-83a6ce9ad4b1828e163dc7172ef603201b748473.zip
lower case direcotry(no code changed)
Signed-off-by: Nikola Pajkovsky <npajkovs@redhat.com>
Diffstat (limited to 'lib/utils/DebugDump.cpp')
-rw-r--r--lib/utils/DebugDump.cpp521
1 files changed, 521 insertions, 0 deletions
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 */
+ }
+}