tools/enigma2.sh.in: add support for libpassthrough, add hook to execute scripts...
[enigma2.git] / main / bsod.cpp
index dd274eca7485be54997158edc034aa8694b14937..a1194328291fba1b592021f8414d28c5ebb0f5f1 100644 (file)
@@ -1,41 +1,52 @@
-#include <string.h>
-#include <signal.h>
+#include <csignal>
+#include <fstream>
+#include <sstream>
+#include <lib/base/eenv.h>
+#include <lib/base/eerror.h>
+#include <lib/base/nconfig.h>
+#include <lib/gdi/gmaindc.h>
+
+#if defined(__MIPSEL__)
 #include <asm/ptrace.h>
+#else
+#warning "no oops support!"
+#define NO_OOPS_SUPPORT
+#endif
 
-#include <lib/base/eerror.h>
-#include <lib/base/smartptr.h>
-#include <lib/gdi/grc.h>
-#include <lib/gdi/gfbdc.h>
+#include "xmlgenerator.h"
+#include "version_info.h"
 
 /************************************************/
 
 #define CRASH_EMAILADDR "crashlog@dream-multimedia-tv.de"
+#define INFOFILE "/maintainer.info"
 
 #define RINGBUFFER_SIZE 16384
 static char ringbuffer[RINGBUFFER_SIZE];
-static int ringbuffer_head;
+static unsigned int ringbuffer_head;
 
-static void addToLogbuffer(const char *data, int len)
+static void addToLogbuffer(const char *data, unsigned int len)
 {
        while (len)
        {
-               int remaining = RINGBUFFER_SIZE - ringbuffer_head;
-       
+               unsigned int remaining = RINGBUFFER_SIZE - ringbuffer_head;
+
                if (remaining > len)
                        remaining = len;
-       
+
                memcpy(ringbuffer + ringbuffer_head, data, remaining);
                len -= remaining;
                data += remaining;
                ringbuffer_head += remaining;
-               if (ringbuffer_head >= RINGBUFFER_SIZE)
+               ASSERT(ringbuffer_head <= RINGBUFFER_SIZE);
+               if (ringbuffer_head == RINGBUFFER_SIZE)
                        ringbuffer_head = 0;
        }
 }
 
-static std::string getLogBuffer()
+static const std::string getLogBuffer()
 {
-       int begin = ringbuffer_head;
+       unsigned int begin = ringbuffer_head;
        while (ringbuffer[begin] == 0)
        {
                ++begin;
@@ -44,12 +55,11 @@ static std::string getLogBuffer()
                if (begin == ringbuffer_head)
                        return "";
        }
+
        if (begin < ringbuffer_head)
                return std::string(ringbuffer + begin, ringbuffer_head - begin);
        else
-       {
                return std::string(ringbuffer + begin, RINGBUFFER_SIZE - begin) + std::string(ringbuffer, ringbuffer_head);
-       }
 }
 
 static void addToLogbuffer(int level, const std::string &log)
@@ -57,102 +67,243 @@ static void addToLogbuffer(int level, const std::string &log)
        addToLogbuffer(log.c_str(), log.size());
 }
 
+static const std::string getConfigString(const std::string &key, const std::string &defaultValue)
+{
+       std::string value;
+
+       ePythonConfigQuery::getConfigValue(key.c_str(), value);
+       //we get at least the default value if python is still alive
+       if (!value.empty())
+               return value;
 
-extern std::string getLogBuffer();
+       value = defaultValue;
 
-void bsodFatal()
+       // get value from enigma2 settings file
+       std::ifstream in(eEnv::resolve("${sysconfdir}/enigma2/settings").c_str());
+       if (in.good()) {
+               do {
+                       std::string line;
+                       std::getline(in, line);
+                       size_t size = key.size();
+                       if (!key.compare(0, size, line) && line[size] == '=') {
+                               value = line.substr(size + 1);
+                               break;
+                       }
+               } while (in.good());
+               in.close();
+       }
+
+       return value;
+}
+
+static bool getConfigBool(const std::string &key, bool defaultValue)
+{
+       std::string value = getConfigString(key, defaultValue ? "true" : "false");
+       const char *cvalue = value.c_str();
+
+       if (!strcasecmp(cvalue, "true"))
+               return true;
+       if (!strcasecmp(cvalue, "false"))
+               return false;
+
+       return defaultValue;
+}
+
+void bsodFatal(const char *component)
 {
-       char logfile[128];
-       sprintf(logfile, "/media/hdd/enigma2_crash_%u.log", (unsigned int)time(0));
-       FILE *f = fopen(logfile, "wb");
+       std::ostringstream os;
+       os << time(0);
+
+       std::string logfile("/media/hdd/enigma2_crash_" + os.str() + ".log");
+
+       FILE *f = fopen(logfile.c_str(), "wb");
        
        std::string lines = getLogBuffer();
+       
+               /* find python-tracebacks, and extract "  File "-strings */
+       size_t start = 0;
+       
+       std::string crash_emailaddr = CRASH_EMAILADDR;
+       std::string crash_component = "enigma2";
+
+       if (component)
+               crash_component = component;
+       else
+       {
+               while ((start = lines.find("\n  File \"", start)) != std::string::npos)
+               {
+                       start += 9;
+                       size_t end = lines.find("\"", start);
+                       if (end == std::string::npos)
+                               break;
+                       end = lines.rfind("/", end);
+                               /* skip a potential prefix to the path */
+                       unsigned int path_prefix = lines.find("/usr/", start);
+                       if (path_prefix != std::string::npos && path_prefix < end)
+                               start = path_prefix;
+
+                       if (end == std::string::npos)
+                               break;
+
+                       std::string filename(lines.substr(start, end - start) + INFOFILE);
+                       std::ifstream in(filename.c_str());
+                       if (in.good()) {
+                               std::getline(in, crash_emailaddr) && std::getline(in, crash_component);
+                               in.close();
+                       }
+               }
+       }
 
        if (f)
        {
                time_t t = time(0);
-               fprintf(f, "enigma2 crashed on %s", ctime(&t));
-               fprintf(f, "please email this file to " CRASH_EMAILADDR "\n");
-               std::string buffer = getLogBuffer();
-               fwrite(buffer.c_str(), buffer.size(), 1, f);
+               struct tm tm;
+               char tm_str[32];
+
+               localtime_r(&t, &tm);
+               strftime(tm_str, sizeof(tm_str), "%a %b %_d %T %Y", &tm);
+
+               XmlGenerator xml(f);
+
+               xml.open("opendreambox");
+
+               xml.open("enigma2");
+               xml.string("crashdate", tm_str);
+               xml.string("compiledate", __DATE__);
+               xml.string("contactemail", crash_emailaddr);
+               xml.comment("Please email this crashlog to above address");
+
+               xml.string("skin", getConfigString("config.skin.primary_skin", "Default Skin"));
+               xml.string("sourcedate", enigma2_date);
+               xml.string("branch", enigma2_branch);
+               xml.string("rev", enigma2_rev);
+               xml.string("version", PACKAGE_VERSION);
+               xml.close();
+
+               xml.open("image");
+               xml.stringFromFile("dreamboxmodel", "/proc/stb/info/model");
+               xml.stringFromFile("kernelcmdline", "/proc/cmdline");
+               xml.stringFromFile("nimsockets", "/proc/bus/nim_sockets");
+               if (!getConfigBool("config.plugins.crashlogautosubmit.sendAnonCrashlog", true)) {
+                       xml.cDataFromFile("dreamboxca", "/proc/stb/info/ca");
+                       xml.cDataFromFile("enigma2settings", eEnv::resolve("${sysconfdir}/enigma2/settings"), ".password=");
+               }
+               if (getConfigBool("config.plugins.crashlogautosubmit.addNetwork", false)) {
+                       xml.cDataFromFile("networkinterfaces", "/etc/network/interfaces");
+                       xml.cDataFromFile("dns", "/etc/resolv.conf");
+                       xml.cDataFromFile("defaultgateway", "/etc/default_gw");
+               }
+               if (getConfigBool("config.plugins.crashlogautosubmit.addWlan", false))
+                       xml.cDataFromFile("wpasupplicant", "/etc/wpa_supplicant.conf");
+               xml.cDataFromFile("imageversion", "/etc/image-version");
+               xml.cDataFromFile("imageissue", "/etc/issue.net");
+               xml.close();
+
+               xml.open("software");
+               xml.cDataFromCmd("enigma2software", "opkg list_installed | grep enigma2");
+               xml.cDataFromCmd("dreamboxsoftware", "opkg list_installed | grep dream");
+               xml.cDataFromCmd("gstreamersoftware", "opkg list_installed | grep gst");
+               xml.close();
+
+               xml.open("crashlogs");
+               xml.cDataFromString("enigma2crashlog", getLogBuffer());
+               xml.cDataFromCmd("pythonMD5sum", "find " + eEnv::resolve("${libdir}/enigma2/python/") + " -name \"*.py\" | xargs md5sum");
+               xml.close();
+
+               xml.close();
+
                fclose(f);
        }
+
+       ePtr<gMainDC> my_dc;
+       gMainDC::getInstance(my_dc);
        
-       ePtr<gFBDC> my_dc;
-       gFBDC::getInstance(my_dc);
+       gPainter p(my_dc);
+       p.resetOffset();
+       p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
+       p.setBackgroundColor(gRGB(0x008000));
+       p.setForegroundColor(gRGB(0xFFFFFF));
+
+       ePtr<gFont> font = new gFont("Regular", 20);
+       p.setFont(font);
+       p.clear();
+
+       eRect usable_area = eRect(100, 70, my_dc->size().width() - 150, 100);
        
+       std::string text("We are really sorry. Your Dreambox encountered "
+               "a software problem, and needs to be restarted. "
+               "Please send the logfile created in /hdd/ to " + crash_emailaddr + ".\n"
+               "Your Dreambox restarts in 10 seconds!\n"
+               "Component: " + crash_component);
+
+       p.renderText(usable_area, text.c_str(), gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
+
+       usable_area = eRect(100, 170, my_dc->size().width() - 180, my_dc->size().height() - 20);
+
+       int i;
+
+       start = std::string::npos + 1;
+       for (i=0; i<20; ++i)
        {
-               gPainter p(my_dc);
-               p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
-               p.setBackgroundColor(gRGB(0x0000C0));
-               p.setForegroundColor(gRGB(0xFFFFFF));
-       
-               ePtr<gFont> font = new gFont("Regular", 20);
-               p.setFont(font);
-               p.clear();
-       
-               eRect usable_area = eRect(100, 70, my_dc->size().width() - 200, 100);
-       
-               p.renderText(usable_area, 
-                       "We are really sorry. Something happened "
-                       "which should not have happened, and "
-                       "resulted in a crash. If you want to help "
-                       "us in improving this situation, please send "
-                       "the logfile created in /hdd/ to " CRASH_EMAILADDR ".", gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
-       
-               usable_area = eRect(100, 170, my_dc->size().width() - 200, my_dc->size().height() - 20);
-       
-               int i;
-       
-               size_t start = std::string::npos + 1;
-               for (i=0; i<20; ++i)
+               start = lines.rfind('\n', start - 1);
+               if (start == std::string::npos)
                {
-                       start = lines.rfind('\n', start - 1);
-                       if (start == std::string::npos)
-                       {
-                               start = 0;
-                               break;
-                       }
+                       start = 0;
+                       break;
                }
-       
-               font = new gFont("Regular", 14);
-               p.setFont(font);
-       
-               p.renderText(usable_area, 
-                       lines.substr(start), gPainter::RT_HALIGN_LEFT);
        }
 
+       font = new gFont("Regular", 14);
+       p.setFont(font);
+
+       p.renderText(usable_area, 
+               lines.substr(start), gPainter::RT_HALIGN_LEFT);
+       sleep(10);
+
        raise(SIGKILL);
 }
 
 #if defined(__MIPSEL__)
 void oops(const mcontext_t &context, int dumpcode)
 {
-       eDebug("PC: %08lx, vaddr: %08lx", (unsigned long)context.pc, (unsigned long)context.badvaddr);
+       eDebug("PC: %08lx", (unsigned long)context.pc);
+       int i;
+       for (i=0; i<32; ++i)
+       {
+               eDebugNoNewLine(" %08x", (int)context.gregs[i]);
+               if ((i&3) == 3)
+                       eDebug("");
+       }
+               /* this is temporary debug stuff. */
+       if (dumpcode && ((unsigned long)context.pc) > 0x10000) /* not a zero pointer */
+       {
+               eDebug("As a final action, i will try to dump a bit of code.");
+               eDebug("I just hope that this won't crash.");
+               int i;
+               eDebugNoNewLine("%08lx:", (unsigned long)context.pc);
+               for (i=0; i<0x20; ++i)
+                       eDebugNoNewLine(" %02x", ((unsigned char*)context.pc)[i]);
+               eDebug(" (end)");
+       }
 }
-#else
-#warning "no oops support!"
-#error bla
-#define NO_OOPS_SUPPORT
 #endif
 
 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
 {
-       ucontext_t *uc = (ucontext_t*)ctx;
-       eDebug("KILLED BY signal %d", signum);
 #ifndef NO_OOPS_SUPPORT
-       oops(uc->uc_mcontext, signum == SIGSEGV);
+       ucontext_t *uc = (ucontext_t*)ctx;
+
+       oops(uc->uc_mcontext, signum == SIGSEGV || signum == SIGABRT);
 #endif
        eDebug("-------");
-       bsodFatal();
+       bsodFatal("enigma2, signal");
 }
 
 void bsodCatchSignals()
 {
        struct sigaction act;
-       act.sa_handler = SIG_DFL;
        act.sa_sigaction = handleFatalSignal;
-       act.sa_flags = 0;
+       act.sa_flags = SA_RESTART | SA_SIGINFO;
        if (sigemptyset(&act.sa_mask) == -1)
                perror("sigemptyset");
        
@@ -160,6 +311,7 @@ void bsodCatchSignals()
        sigaction(SIGSEGV, &act, 0);
        sigaction(SIGILL, &act, 0);
        sigaction(SIGBUS, &act, 0);
+       sigaction(SIGABRT, &act, 0);
 }
 
 void bsodLogInit()