From 83a6ce9ad4b1828e163dc7172ef603201b748473 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 10 Aug 2010 10:21:25 +0200 Subject: lower case direcotry(no code changed) Signed-off-by: Nikola Pajkovsky --- src/hooks/Makefile.am | 44 +++ src/hooks/abrt-hook-ccpp.cpp | 553 +++++++++++++++++++++++++++++++++ src/hooks/abrt.pth | 1 + src/hooks/abrt_exception_handler.py.in | 167 ++++++++++ src/hooks/dumpoops.cpp | 125 ++++++++ 5 files changed, 890 insertions(+) create mode 100644 src/hooks/Makefile.am create mode 100644 src/hooks/abrt-hook-ccpp.cpp create mode 100644 src/hooks/abrt.pth create mode 100644 src/hooks/abrt_exception_handler.py.in create mode 100644 src/hooks/dumpoops.cpp (limited to 'src/hooks') diff --git a/src/hooks/Makefile.am b/src/hooks/Makefile.am new file mode 100644 index 00000000..55ffc446 --- /dev/null +++ b/src/hooks/Makefile.am @@ -0,0 +1,44 @@ +libexec_PROGRAMS = abrt-hook-ccpp +bin_PROGRAMS = dumpoops + +# abrt-hook-ccpp +abrt_hook_ccpp_SOURCES = abrt-hook-ccpp.cpp +abrt_hook_ccpp_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -D_GNU_SOURCE +abrt_hook_ccpp_LDADD = \ + ../../lib/utils/libABRTUtils.la + +# dumpoops +dumpoops_SOURCES = dumpoops.cpp +dumpoops_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -I$(srcdir)/../../lib/plugins \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -D_GNU_SOURCE +# build will succeed, but at runtime plugins do need ABRT*d*Utils +dumpoops_LDADD = \ + ../../lib/utils/libABRTUtils.la \ + ../../lib/utils/libABRTdUtils.la + +python_PYTHON = abrt.pth abrt_exception_handler.py +EXTRA_DIST = abrt_exception_handler.py.in $(man_MANS) + +CLEANFILES := $(notdir $(wildcard *~)) $(notdir $(wildcard *\#)) $(notdir $(wildcard \.\#*)) $(notdir $(wildcard *.pyc)) + +# Must be synchronized with another sed call below. +abrt_exception_handler.py: + sed s,\@VAR_RUN\@,\"$(VAR_RUN)\",g abrt_exception_handler.py.in > abrt_exception_handler.py + +# RPM fix: we need to regenerate abrt_exception_handler.py, because it has the default ddir +install-data-local: + sed s,\@VAR_RUN\@,\"$(VAR_RUN)\",g abrt_exception_handler.py.in > abrt_exception_handler.py diff --git a/src/hooks/abrt-hook-ccpp.cpp b/src/hooks/abrt-hook-ccpp.cpp new file mode 100644 index 00000000..e53007a4 --- /dev/null +++ b/src/hooks/abrt-hook-ccpp.cpp @@ -0,0 +1,553 @@ +/* + abrt-hook-ccpp.cpp - the hook for C/C++ crashing program + + Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com) + Copyright (C) 2009 RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "abrtlib.h" +#include "hooklib.h" +#include "debug_dump.h" +#include "crash_types.h" +#include "abrt_exception.h" +#include + +using namespace std; + +static char* malloc_readlink(const char *linkname) +{ + char buf[PATH_MAX + 1]; + int len; + + len = readlink(linkname, buf, sizeof(buf)-1); + if (len >= 0) + { + buf[len] = '\0'; + return xstrdup(buf); + } + return NULL; +} + +/* Custom version of copyfd_xyz, + * one which is able to write into two descriptors at once. + */ +#define CONFIG_FEATURE_COPYBUF_KB 4 +static off_t copyfd_sparse(int src_fd, int dst_fd1, int dst_fd2, off_t size2) +{ + 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 + + while (1) { + ssize_t rd = safe_read(src_fd, buffer, buffer_size); + if (!rd) { /* eof */ + if (last_was_seek) { + if (lseek(dst_fd1, -1, SEEK_CUR) < 0 + || safe_write(dst_fd1, "", 1) != 1 + || (dst_fd2 >= 0 + && (lseek(dst_fd2, -1, SEEK_CUR) < 0 + || safe_write(dst_fd2, "", 1) != 1 + ) + ) + ) { + perror_msg("write error"); + total = -1; + goto out; + } + } + /* all done */ + goto out; + } + if (rd < 0) { + perror_msg("read error"); + total = -1; + goto out; + } + + /* checking sparseness */ + ssize_t cnt = rd; + while (--cnt >= 0) { + if (buffer[cnt] != 0) { + /* not sparse */ + errno = 0; + ssize_t wr1 = full_write(dst_fd1, buffer, rd); + ssize_t wr2 = (dst_fd2 >= 0 ? full_write(dst_fd2, buffer, rd) : rd); + if (wr1 < rd || wr2 < rd) { + perror_msg("write error"); + total = -1; + goto out; + } + last_was_seek = 0; + goto adv; + } + } + /* sparse */ + xlseek(dst_fd1, rd, SEEK_CUR); + if (dst_fd2 >= 0) + xlseek(dst_fd2, rd, SEEK_CUR); + last_was_seek = 1; + adv: + total += rd; + size2 -= rd; + if (size2 < 0) + dst_fd2 = -1; + } + out: + +#if CONFIG_FEATURE_COPYBUF_KB > 4 + if (buffer_size != 4 * 1024) + munmap(buffer, buffer_size); +#endif + return total; +} + +static char* get_executable(pid_t pid, int *fd_p) +{ + char buf[sizeof("/proc/%lu/exe") + sizeof(long)*3]; + + sprintf(buf, "/proc/%lu/exe", (long)pid); + *fd_p = open(buf, O_RDONLY); /* might fail and return -1, it's ok */ + char *executable = malloc_readlink(buf); + /* find and cut off " (deleted)" from the path */ + char *deleted = executable + strlen(executable) - strlen(" (deleted)"); + if (deleted > executable && strcmp(deleted, " (deleted)") == 0) + { + *deleted = '\0'; + log("file %s seems to be deleted", executable); + } + /* find and cut off prelink suffixes from the path */ + char *prelink = executable + strlen(executable) - strlen(".#prelink#.XXXXXX"); + if (prelink > executable && strncmp(prelink, ".#prelink#.", strlen(".#prelink#.")) == 0) + { + log("file %s seems to be a prelink temporary file", executable); + *prelink = '\0'; + } + return executable; +} + +static char* get_cwd(pid_t pid) +{ + char buf[sizeof("/proc/%lu/cwd") + sizeof(long)*3]; + + sprintf(buf, "/proc/%lu/cwd", (long)pid); + return malloc_readlink(buf); +} + +static char core_basename[sizeof("core.%lu") + sizeof(long)*3] = "core"; + +static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) +{ + struct passwd* pw = getpwuid(uid); + gid_t gid = pw ? pw->pw_gid : uid; + xsetegid(gid); + xseteuid(uid); + + errno = 0; + if (user_pwd == NULL + || chdir(user_pwd) != 0 + ) { + perror_msg("can't cd to %s", user_pwd); + return 0; + } + + /* Mimic "core.PID" if requested */ + char buf[] = "0\n"; + int fd = open("/proc/sys/kernel/core_uses_pid", O_RDONLY); + if (fd >= 0) + { + read(fd, buf, sizeof(buf)); + close(fd); + } + if (strcmp(buf, "1\n") == 0) + { + sprintf(core_basename, "core.%lu", (long)pid); + } + + /* man core: + * There are various circumstances in which a core dump file + * is not produced: + * + * [skipped obvious ones] + * The process does not have permission to write the core file. + * ...if a file with the same name exists and is not writable + * or is not a regular file (e.g., it is a directory or a symbolic link). + * + * A file with the same name already exists, but there is more + * than one hard link to that file. + * + * The file system where the core dump file would be created is full; + * or has run out of inodes; or is mounted read-only; + * or the user has reached their quota for the file system. + * + * The RLIMIT_CORE or RLIMIT_FSIZE resource limits for the process + * are set to zero. + * [shouldn't it be checked by kernel? 2.6.30.9-96 doesn't, still + * calls us even if "ulimit -c 0"] + * + * The binary being executed by the process does not have + * read permission enabled. [how we can check it here?] + * + * The process is executing a set-user-ID (set-group-ID) program + * that is owned by a user (group) other than the real + * user (group) ID of the process. [TODO?] + * (However, see the description of the prctl(2) PR_SET_DUMPABLE operation, + * and the description of the /proc/sys/fs/suid_dumpable file in proc(5).) + */ + + /* Do not O_TRUNC: if later checks fail, we do not want to have file already modified here */ + struct stat sb; + errno = 0; + int user_core_fd = open(core_basename, O_WRONLY | O_CREAT | O_NOFOLLOW, 0600); /* kernel makes 0600 too */ + xsetegid(0); + xseteuid(0); + if (user_core_fd < 0 + || fstat(user_core_fd, &sb) != 0 + || !S_ISREG(sb.st_mode) + || sb.st_nlink != 1 + /* kernel internal dumper checks this too: if (inode->i_uid != current->fsuid) , need to mimic? */ + ) { + perror_msg("%s/%s is not a regular file with link count 1", user_pwd, core_basename); + return -1; + } + if (ftruncate(user_core_fd, 0) != 0) { + /* perror first, otherwise unlink may trash errno */ + perror_msg("truncate %s/%s", user_pwd, core_basename); + return -1; + } + + return user_core_fd; +} + +int main(int argc, char** argv) +{ + int i; + struct stat sb; + + if (argc < 5) + { + error_msg_and_die("Usage: %s: DUMPDIR PID SIGNO UID CORE_SIZE_LIMIT", argv[0]); + } + + /* Not needed on 2.6.30. + * At least 2.6.18 has a bug where + * argv[1] = "DUMPDIR PID SIGNO UID CORE_SIZE_LIMIT" + * argv[2] = "PID SIGNO UID CORE_SIZE_LIMIT" + * and so on. Fixing it: + */ + for (i = 1; argv[i]; i++) + { + strchrnul(argv[i], ' ')[0] = '\0'; + } + + openlog("abrt", LOG_PID, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; + + errno = 0; + const char* dddir = argv[1]; + pid_t pid = xatoi_u(argv[2]); + const char* signal_str = argv[3]; + int signal_no = xatoi_u(argv[3]); + uid_t uid = xatoi_u(argv[4]); + off_t ulimit_c = strtoull(argv[5], NULL, 10); + if (ulimit_c < 0) /* unlimited? */ + { + /* set to max possible >0 value */ + ulimit_c = ~((off_t)1 << (sizeof(off_t)*8-1)); + } + if (errno || pid <= 0) + { + error_msg_and_die("pid '%s' or limit '%s' is bogus", argv[2], argv[5]); + } + + int src_fd_binary; + char* executable = get_executable(pid, &src_fd_binary); + if (executable == NULL) + { + perror_msg_and_die("can't read /proc/%lu/exe link", (long)pid); + } + if (strstr(executable, "/abrt-hook-ccpp")) + { + error_msg_and_die("pid %lu is '%s', not dumping it to avoid recursion", + (long)pid, executable); + } + + char *user_pwd = get_cwd(pid); /* may be NULL on error */ + + /* Parse abrt.conf and plugins/CCpp.conf */ + unsigned setting_MaxCrashReportsSize = 0; + bool setting_MakeCompatCore = false; + bool setting_SaveBinaryImage = false; + parse_conf(CONF_DIR"/plugins/CCpp.conf", &setting_MaxCrashReportsSize, &setting_MakeCompatCore, &setting_SaveBinaryImage); + if (!setting_SaveBinaryImage && src_fd_binary >= 0) + { + close(src_fd_binary); + src_fd_binary = -1; + } + + /* Open a fd to compat coredump, if requested and is possible */ + int user_core_fd = -1; + if (setting_MakeCompatCore && ulimit_c != 0) + user_core_fd = open_user_core(user_pwd, uid, pid); + + const char *signame = NULL; + /* Tried to use array for this but C++ does not support v[] = { [IDX] = "str" } */ + switch (signal_no) + { + case SIGILL : signame = "ILL" ; break; + case SIGFPE : signame = "FPE" ; break; + case SIGSEGV: signame = "SEGV"; break; + case SIGBUS : signame = "BUS" ; break; //Bus error (bad memory access) + case SIGABRT: signame = "ABRT"; break; //usually when abort() was called + //case SIGQUIT: signame = "QUIT"; break; //Quit from keyboard + //case SIGSYS : signame = "SYS" ; break; //Bad argument to routine (SVr4) + //case SIGTRAP: signame = "TRAP"; break; //Trace/breakpoint trap + //case SIGXCPU: signame = "XCPU"; break; //CPU time limit exceeded (4.2BSD) + //case SIGXFSZ: signame = "XFSZ"; break; //File size limit exceeded (4.2BSD) + default: goto create_user_core; // not a signal we care about + } + + if (!daemon_is_ok()) + { + /* not an error, exit with exitcode 0 */ + log("abrt daemon is not running. If it crashed, " + "/proc/sys/kernel/core_pattern contains a stale value, " + "consider resetting it to 'core'" + ); + goto create_user_core; + } + + try + { + if (setting_MaxCrashReportsSize > 0) + { + check_free_space(setting_MaxCrashReportsSize); + } + + char path[PATH_MAX]; + + /* Check /var/spool/abrt/last-ccpp marker, do not dump repeated crashes + * if they happen too often. Else, write new marker value. + */ + snprintf(path, sizeof(path), "%s/last-ccpp", dddir); + int fd = open(path, O_RDWR | O_CREAT, 0600); + if (fd >= 0) + { + int sz; + fstat(fd, &sb); /* !paranoia. this can't fail. */ + + if (sb.st_size != 0 /* if it wasn't created by us just now... */ + && (unsigned)(time(NULL) - sb.st_mtime) < 20 /* and is relatively new [is 20 sec ok?] */ + ) { + sz = read(fd, path, sizeof(path)-1); /* (ab)using path as scratch buf */ + if (sz > 0) + { + path[sz] = '\0'; + if (strcmp(executable, path) == 0) + { + error_msg("not dumping repeating crash in '%s'", executable); + if (setting_MakeCompatCore) + goto create_user_core; + return 1; + } + } + lseek(fd, 0, SEEK_SET); + } + sz = write(fd, executable, strlen(executable)); + if (sz >= 0) + ftruncate(fd, sz); + close(fd); + } + + if (strstr(executable, "/abrtd")) + { + /* If abrtd crashes, we don't want to create a _directory_, + * since that can make new copy of abrtd to process it, + * and maybe crash again... + * Unlike dirs, mere files are ignored by abrtd. + */ + snprintf(path, sizeof(path), "%s/abrtd-coredump", dddir); + int abrt_core_fd = xopen3(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + off_t core_size = copyfd_eof(STDIN_FILENO, abrt_core_fd, COPYFD_SPARSE); + if (core_size < 0 || fsync(abrt_core_fd) != 0) + { + unlink(path); + /* copyfd_eof logs the error including errno string, + * but it does not log file name */ + error_msg_and_die("error saving coredump to %s", path); + } + log("saved core dump of pid %lu (%s) to %s (%llu bytes)", (long)pid, executable, path, (long long)core_size); + return 0; + } + + unsigned path_len = snprintf(path, sizeof(path), "%s/ccpp-%ld-%lu.new", + dddir, (long)time(NULL), (long)pid); + if (path_len >= (sizeof(path) - sizeof("/"FILENAME_COREDUMP))) + return 1; + + CDebugDump dd; + char *cmdline = get_cmdline(pid); /* never NULL */ + char *reason = xasprintf("Process %s was killed by signal %s (SIG%s)", executable, signal_str, signame ? signame : signal_str); + dd.Create(path, uid); + dd.SaveText(FILENAME_ANALYZER, "CCpp"); + dd.SaveText(FILENAME_EXECUTABLE, executable); + dd.SaveText(FILENAME_CMDLINE, cmdline); + dd.SaveText(FILENAME_REASON, reason); + free(cmdline); + free(reason); + + if (src_fd_binary > 0) + { + strcpy(path + path_len, "/"FILENAME_BINARY); + int dst_fd_binary = xopen3(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + off_t sz = copyfd_eof(src_fd_binary, dst_fd_binary, COPYFD_SPARSE); + if (sz < 0 || fsync(dst_fd_binary) != 0) + { + unlink(path); + error_msg_and_die("error saving binary image to %s", path); + } + close(dst_fd_binary); + close(src_fd_binary); + } + + /* We need coredumps to be readable by all, because + * when abrt daemon processes coredump, + * process producing backtrace is run under the same UID + * as the crashed process. + * Thus 644, not 600 */ + strcpy(path + path_len, "/"FILENAME_COREDUMP); + int abrt_core_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (abrt_core_fd < 0) + { + int sv_errno = errno; + dd.Delete(); + dd.Close(); + if (user_core_fd >= 0) + { + xchdir(user_pwd); + unlink(core_basename); + } + errno = sv_errno; + perror_msg_and_die("can't open '%s'", path); + } + + /* We write both coredumps at once. + * We can't write user coredump first, since it might be truncated + * and thus can't be copied and used as abrt coredump; + * and if we write abrt coredump first and then copy it as user one, + * then we have a race when process exits but coredump does not exist yet: + * $ echo -e '#include\nmain(){raise(SIGSEGV);}' | gcc -o test -x c - + * $ rm -f core*; ulimit -c unlimited; ./test; ls -l core* + * 21631 Segmentation fault (core dumped) ./test + * ls: cannot access core*: No such file or directory <=== BAD + */ +//TODO: fchown abrt_core_fd to uid:abrt? +//Currently it is owned by 0:0 but is readable by anyone, so the owner +//of the crashed binary still can access it, as he has +//r-x access to the dump dir. + off_t core_size = copyfd_sparse(STDIN_FILENO, abrt_core_fd, user_core_fd, ulimit_c); + if (core_size < 0 || fsync(abrt_core_fd) != 0) + { + unlink(path); + dd.Delete(); + dd.Close(); + if (user_core_fd >= 0) + { + xchdir(user_pwd); + unlink(core_basename); + } + /* copyfd_sparse logs the error including errno string, + * but it does not log file name */ + error_msg_and_die("error writing %s", path); + } + log("saved core dump of pid %lu (%s) to %s (%llu bytes)", (long)pid, executable, path, (long long)core_size); + if (user_core_fd >= 0 && core_size >= ulimit_c) + { + /* user coredump is too big, nuke it */ + xchdir(user_pwd); + unlink(core_basename); + } + + /* We close dumpdir before we start catering for crash storm case. + * Otherwise, delete_debug_dump_dir's from other concurrent + * CCpp's won't be able to delete our dump (their delete_debug_dump_dir + * will wait for us), and we won't be able to delete their dumps. + * Classic deadlock. + */ + dd.Close(); + path[path_len] = '\0'; /* path now contains only directory name */ + char *newpath = xstrndup(path, path_len - (sizeof(".new")-1)); + if (rename(path, newpath) == 0) + strcpy(path, newpath); + free(newpath); + + /* rhbz#539551: "abrt going crazy when crashing process is respawned" */ + if (setting_MaxCrashReportsSize > 0) + { + trim_debug_dumps(setting_MaxCrashReportsSize, path); + } + + return 0; + } + catch (CABRTException& e) + { + error_msg_and_die("%s", e.what()); + } + catch (std::exception& e) + { + error_msg_and_die("%s", e.what()); + } + + /* We didn't create abrt dump, but may need to create compat coredump */ + create_user_core: + if (user_core_fd < 0) + return 0; + + off_t core_size = copyfd_size(STDIN_FILENO, user_core_fd, ulimit_c, COPYFD_SPARSE); + if (core_size < 0 || fsync(user_core_fd) != 0) { + /* perror first, otherwise unlink may trash errno */ + perror_msg("error writing %s/%s", user_pwd, core_basename); + xchdir(user_pwd); + unlink(core_basename); + return 1; + } + if (core_size >= ulimit_c) + { + xchdir(user_pwd); + unlink(core_basename); + return 1; + } + log("saved core dump of pid %lu to %s/%s (%llu bytes)", (long)pid, user_pwd, core_basename, (long long)core_size); + + return 0; +} diff --git a/src/hooks/abrt.pth b/src/hooks/abrt.pth new file mode 100644 index 00000000..39fc292e --- /dev/null +++ b/src/hooks/abrt.pth @@ -0,0 +1 @@ +import abrt_exception_handler diff --git a/src/hooks/abrt_exception_handler.py.in b/src/hooks/abrt_exception_handler.py.in new file mode 100644 index 00000000..dd6fbaed --- /dev/null +++ b/src/hooks/abrt_exception_handler.py.in @@ -0,0 +1,167 @@ +#:mode=python: +# -*- coding: utf-8 -*- +## Copyright (C) 2001-2005 Red Hat, Inc. +## Copyright (C) 2001-2005 Harald Hoyer +## Copyright (C) 2009 Jiri Moskovcak + +## 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +""" +Module for the ABRT exception handling hook +""" + +import sys +import os +import syslog +import subprocess +import socket + +def write_dump(pid, tb): + executable = "Exception raised from python shell" + if sys.argv[0]: + executable = os.path.abspath(sys.argv[0]) + + # Open ABRT daemon's socket and write data to it. + try: + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(@VAR_RUN@ + "/abrt/abrt.socket") + s.sendall("PID=%s\0" % pid) + s.sendall("EXECUTABLE=%s\0" % executable) + s.sendall("ANALYZER=Python\0") + s.sendall("BASENAME=pyhook\0") + # This handler puts a short(er) crash descr in 1st line of the backtrace. + # Example: + # CCMainWindow.py:1::ZeroDivisionError: integer division or modulo by zero + s.sendall("REASON=%s\0" % tb.splitlines()[0]) + s.sendall("BACKTRACE=%s\0" % tb) + s.sendall("DONE\0") + s.close() + except Exception, ex: + syslog.syslog("can't communicate with ABRT daemon, is it running? %s", str(ex)) + +def handleMyException((etype, value, tb)): + """ + The exception handling function. + + progname - the name of the application + version - the version of the application + """ + + # restore original exception handler + sys.excepthook = sys.__excepthook__ # pylint: disable-msg=E1101 + # ignore uncaught ctrl-c + if etype == KeyboardInterrupt: + return sys.__excepthook__(etype, value, tb) + + try: + import os + import os.path + import traceback + import errno + + # EPIPE is not a crash, it happens all the time + # Testcase: script.py | true, where script.py is: + ## #!/usr/bin/python + ## import os + ## import time + ## time.sleep(1) + ## os.write(1, "Hello\n") # print "Hello" wouldn't be the same + # + if etype == IOError or etype == OSError: + if value.errno == errno.EPIPE: + return sys.__excepthook__(etype, value, tb) + + # "-c" appears in this case: + # $ python -c 'import sys; print "argv0 is:%s" % sys.argv[0]' + # argv0 is:-c + if not sys.argv[0] or sys.argv[0] == "-c": + # Looks like interactive Python - abort dumping + syslog.syslog("abrt: detected unhandled Python exception") + raise Exception + syslog.syslog("abrt: detected unhandled Python exception in %s" % sys.argv[0]) + if sys.argv[0][0] != "/": + # Relative path - can't reliably determine package + # this script belongs to - abort dumping + # TODO: check abrt.conf and abort only if + # ProcessUnpackaged = no? + raise Exception + + elist = traceback.format_exception(etype, value, tb) + tblast = traceback.extract_tb(tb, limit=None) + if len(tblast): + tblast = tblast[len(tblast)-1] + extxt = traceback.format_exception_only(etype, value) + if tblast and len(tblast) > 3: + ll = [] + ll.extend(tblast[:3]) + ll[0] = os.path.basename(tblast[0]) + tblast = ll + + ntext = "" + for t in tblast: + ntext += str(t) + ":" + + text = ntext + text += extxt[0] + text += "\n" + text += "".join(elist) + + trace = tb + while trace.tb_next: + trace = trace.tb_next + frame = trace.tb_frame + text += ("\nLocal variables in innermost frame:\n") + try: + for (key, val) in frame.f_locals.items(): + text += "%s: %s\n" % (key, repr(val)) + except: + pass + + # add coredump saving + write_dump(os.getpid(), text) + + except: + # silently ignore any error in this hook, + # to not interfere with the python scripts + pass + + return sys.__excepthook__(etype, value, tb) + + +def installExceptionHandler(): + """ + Install the exception handling function. + """ + sys.excepthook = lambda etype, value, tb: handleMyException((etype, value, tb)) + +# install the exception handler when the abrt_exception_handler +# module is imported +try: + installExceptionHandler() +except Exception, e: + # TODO: log errors? + # OTOH, if abrt is deinstalled uncleanly + # and this file (sitecustomize.py) exists but + # abrt_exception_handler module does not exist, we probably + # don't want to irritate admins... + pass + +if __name__ == '__main__': + # test exception raised to show the effect + div0 = 1 / 0 # pylint: disable-msg=W0612 + sys.exit(0) + + +__author__ = "Harald Hoyer " diff --git a/src/hooks/dumpoops.cpp b/src/hooks/dumpoops.cpp new file mode 100644 index 00000000..a2d2353a --- /dev/null +++ b/src/hooks/dumpoops.cpp @@ -0,0 +1,125 @@ +/* + 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. + + Authors: + Denys Vlasenko + Zdenek Prikryl +*/ + +#include "abrtlib.h" +#include "abrt_types.h" +#include "abrt_exception.h" +#include "KerneloopsScanner.h" +#include + +#define LOADSYM(fp, name) \ +do { \ + fp = (typeof(fp)) (dlsym(handle, name)); \ + if (!fp) \ + perror_msg_and_die(PLUGINS_LIB_DIR"/libKerneloopsScanner.so has no %s", name); \ +} while (0) + + +int main(int argc, char **argv) +{ + char *program_name = strrchr(argv[0], '/'); + program_name = program_name ? program_name + 1 : argv[0]; + + /* Parse options */ + bool opt_d = 0, opt_s = 0; + int opt; + while ((opt = getopt(argc, argv, "dsv")) != -1) { + switch (opt) { + case 'd': + opt_d = 1; + break; + case 's': + opt_s = 1; + break; + case 'v': + /* Kerneloops code uses VERB3, thus: */ + g_verbose = 3; + break; + default: + usage: + error_msg_and_die( + "Usage: %s [-dsv] FILE\n\n" + "Options:\n" + "\t-d\tCreate ABRT dump for every oops found\n" + "\t-s\tPrint found oopses on standard output\n" + "\t-v\tVerbose\n" + , program_name + ); + } + } + argv += optind; + if (!argv[0]) + goto usage; + + msg_prefix = xasprintf("%s: ", program_name); + + /* Load KerneloopsScanner plugin */ +// const plugin_info_t *plugin_info; + CPlugin* (*plugin_newf)(void); + int (*scan_syslog_file)(vector_string_t& oopsList, const char *filename, time_t *last_changed_p); + void (*save_oops_to_debug_dump)(const vector_string_t& oopsList); + void *handle; + + errno = 0; +//TODO: use it directly, not via dlopen? + handle = dlopen(PLUGINS_LIB_DIR"/libKerneloopsScanner.so", RTLD_NOW); + if (!handle) + perror_msg_and_die("can't load %s", PLUGINS_LIB_DIR"/libKerneloopsScanner.so"); + +// LOADSYM(plugin_info, "plugin_info"); + LOADSYM(plugin_newf, "plugin_new"); + LOADSYM(scan_syslog_file, "scan_syslog_file"); + LOADSYM(save_oops_to_debug_dump, "save_oops_to_debug_dump"); + +// CKerneloopsScanner* scanner = (CKerneloopsScanner*) plugin_newf(); +// scanner->Init(); +// scanner->LoadSettings(path); + + /* Use it: parse and dump the oops */ + vector_string_t oopsList; + int cnt = scan_syslog_file(oopsList, argv[0], NULL); + log("found oopses: %d", cnt); + + if (cnt > 0) { + if (opt_s) { + int i = 0; + while (i < oopsList.size()) { + printf("\nVersion: %s", oopsList[i].c_str()); + i++; + } + } + if (opt_d) { + log("dumping oopses"); + try { + save_oops_to_debug_dump(oopsList); + } + catch (CABRTException& e) { + fprintf(stderr, "Error: %s\n", e.what()); + return 1; + } + } + } + + /*dlclose(handle); - why bother? */ + return 0; +} -- cgit