dca4a7c3c11e577c70120699f52e01a1abd10785
[enigma2.git] / main / bsod.cpp
1 #include <fstream>
2 #include <sstream>
3 #include <lib/base/eenv.h>
4 #include <lib/base/eerror.h>
5 #include <lib/base/nconfig.h>
6 #include <lib/gdi/gmaindc.h>
7
8 #if defined(__MIPSEL__)
9 #include <asm/ptrace.h>
10 #else
11 #warning "no oops support!"
12 #define NO_OOPS_SUPPORT
13 #endif
14
15 #include "xmlgenerator.h"
16 #include "version_info.h"
17
18 /************************************************/
19
20 #define CRASH_EMAILADDR "crashlog@dream-multimedia-tv.de"
21 #define INFOFILE "/maintainer.info"
22
23 #define RINGBUFFER_SIZE 16384
24 static char ringbuffer[RINGBUFFER_SIZE];
25 static unsigned int ringbuffer_head;
26
27 static void addToLogbuffer(const char *data, unsigned int len)
28 {
29         while (len)
30         {
31                 unsigned int remaining = RINGBUFFER_SIZE - ringbuffer_head;
32
33                 if (remaining > len)
34                         remaining = len;
35
36                 memcpy(ringbuffer + ringbuffer_head, data, remaining);
37                 len -= remaining;
38                 data += remaining;
39                 ringbuffer_head += remaining;
40                 ASSERT(ringbuffer_head <= RINGBUFFER_SIZE);
41                 if (ringbuffer_head == RINGBUFFER_SIZE)
42                         ringbuffer_head = 0;
43         }
44 }
45
46 static const std::string getLogBuffer()
47 {
48         unsigned int begin = ringbuffer_head;
49         while (ringbuffer[begin] == 0)
50         {
51                 ++begin;
52                 if (begin == RINGBUFFER_SIZE)
53                         begin = 0;
54                 if (begin == ringbuffer_head)
55                         return "";
56         }
57
58         if (begin < ringbuffer_head)
59                 return std::string(ringbuffer + begin, ringbuffer_head - begin);
60         else
61                 return std::string(ringbuffer + begin, RINGBUFFER_SIZE - begin) + std::string(ringbuffer, ringbuffer_head);
62 }
63
64 static void addToLogbuffer(int level, const std::string &log)
65 {
66         addToLogbuffer(log.c_str(), log.size());
67 }
68
69 static const std::string getConfigString(const std::string &key, const std::string &defaultValue)
70 {
71         std::string value;
72
73         ePythonConfigQuery::getConfigValue(key.c_str(), value);
74         //we get at least the default value if python is still alive
75         if (!value.empty())
76                 return value;
77
78         value = defaultValue;
79
80         // get value from enigma2 settings file
81         std::ifstream in(eEnv::resolve("${sysconfdir}/enigma2/settings").c_str());
82         if (in.good()) {
83                 do {
84                         std::string line;
85                         std::getline(in, line);
86                         size_t size = key.size();
87                         if (!key.compare(0, size, line) && line[size] == '=') {
88                                 value = line.substr(size + 1);
89                                 break;
90                         }
91                 } while (in.good());
92                 in.close();
93         }
94
95         return value;
96 }
97
98 static bool getConfigBool(const std::string &key, bool defaultValue)
99 {
100         std::string value = getConfigString(key, defaultValue ? "true" : "false");
101         const char *cvalue = value.c_str();
102
103         if (!strcasecmp(cvalue, "true"))
104                 return true;
105         if (!strcasecmp(cvalue, "false"))
106                 return false;
107
108         return defaultValue;
109 }
110
111 void bsodFatal(const char *component)
112 {
113         std::ostringstream os;
114         os << time(0);
115
116         std::string logfile("/media/hdd/enigma2_crash_" + os.str() + ".log");
117
118         FILE *f = fopen(logfile.c_str(), "wb");
119         
120         std::string lines = getLogBuffer();
121         
122                 /* find python-tracebacks, and extract "  File "-strings */
123         size_t start = 0;
124         
125         std::string crash_emailaddr = CRASH_EMAILADDR;
126         std::string crash_component = "enigma2";
127
128         if (component)
129                 crash_component = component;
130         else
131         {
132                 while ((start = lines.find("\n  File \"", start)) != std::string::npos)
133                 {
134                         start += 9;
135                         size_t end = lines.find("\"", start);
136                         if (end == std::string::npos)
137                                 break;
138                         end = lines.rfind("/", end);
139                                 /* skip a potential prefix to the path */
140                         unsigned int path_prefix = lines.find("/usr/", start);
141                         if (path_prefix != std::string::npos && path_prefix < end)
142                                 start = path_prefix;
143
144                         if (end == std::string::npos)
145                                 break;
146
147                         std::string filename(lines.substr(start, end - start) + INFOFILE);
148                         std::ifstream in(filename.c_str());
149                         if (in.good()) {
150                                 std::getline(in, crash_emailaddr) && std::getline(in, crash_component);
151                                 in.close();
152                         }
153                 }
154         }
155
156         if (f)
157         {
158                 time_t t = time(0);
159                 struct tm tm;
160                 char tm_str[32];
161
162                 localtime_r(&t, &tm);
163                 strftime(tm_str, sizeof(tm_str), "%a %b %_d %T %Y", &tm);
164
165                 XmlGenerator xml(f);
166
167                 xml.open("opendreambox");
168
169                 xml.open("enigma2");
170                 xml.string("crashdate", tm_str);
171                 xml.string("compiledate", __DATE__);
172                 xml.string("contactemail", crash_emailaddr);
173                 xml.comment("Please email this crashlog to above address");
174
175                 xml.string("skin", getConfigString("config.skin.primary_skin", "Default Skin"));
176                 xml.string("sourcedate", enigma2_date);
177                 xml.string("branch", enigma2_branch);
178                 xml.string("rev", enigma2_rev);
179                 xml.string("version", PACKAGE_VERSION);
180                 xml.close();
181
182                 xml.open("image");
183                 xml.stringFromFile("dreamboxmodel", "/proc/stb/info/model");
184                 xml.stringFromFile("kernelcmdline", "/proc/cmdline");
185                 if (!getConfigBool("config.plugins.crashlogautosubmit.sendAnonCrashlog", true)) {
186                         xml.cDataFromFile("dreamboxca", "/proc/stb/info/ca");
187                         xml.cDataFromFile("enigma2settings", eEnv::resolve("${sysconfdir}/enigma2/settings"), ".password=");
188                 }
189                 if (getConfigBool("config.plugins.crashlogautosubmit.addNetwork", false)) {
190                         xml.cDataFromFile("networkinterfaces", "/etc/network/interfaces");
191                         xml.cDataFromFile("dns", "/etc/resolv.conf");
192                         xml.cDataFromFile("defaultgateway", "/etc/default_gw");
193                 }
194                 if (getConfigBool("config.plugins.crashlogautosubmit.addWlan", false))
195                         xml.cDataFromFile("wpasupplicant", "/etc/wpa_supplicant.conf");
196                 xml.cDataFromFile("imageversion", "/etc/image-version");
197                 xml.cDataFromFile("imageissue", "/etc/issue.net");
198                 xml.close();
199
200                 xml.open("software");
201                 xml.cDataFromCmd("enigma2software", "ipkg list_installed | grep enigma2");
202                 xml.cDataFromCmd("dreamboxsoftware", "ipkg list_installed | grep dream");
203                 xml.cDataFromCmd("gstreamersoftware", "ipkg list_installed | grep gst");
204                 xml.close();
205
206                 xml.open("crashlogs");
207                 xml.cDataFromString("enigma2crashlog", getLogBuffer());
208                 xml.cDataFromCmd("pythonMD5sum", "find " + eEnv::resolve("${libdir}/enigma2/python/") + " -name \"*.py\" | xargs md5sum");
209                 xml.close();
210
211                 xml.close();
212
213                 fclose(f);
214         }
215
216         ePtr<gMainDC> my_dc;
217         gMainDC::getInstance(my_dc);
218         
219         gPainter p(my_dc);
220         p.resetOffset();
221         p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
222         p.setBackgroundColor(gRGB(0x008000));
223         p.setForegroundColor(gRGB(0xFFFFFF));
224
225         ePtr<gFont> font = new gFont("Regular", 20);
226         p.setFont(font);
227         p.clear();
228
229         eRect usable_area = eRect(100, 70, my_dc->size().width() - 150, 100);
230         
231         std::string text("We are really sorry. Your Dreambox encountered "
232                 "a software problem, and needs to be restarted. "
233                 "Please send the logfile created in /hdd/ to " + crash_emailaddr + ".\n"
234                 "Your Dreambox restarts in 10 seconds!\n"
235                 "Component: " + crash_component);
236
237         p.renderText(usable_area, text.c_str(), gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
238
239         usable_area = eRect(100, 170, my_dc->size().width() - 180, my_dc->size().height() - 20);
240
241         int i;
242
243         start = std::string::npos + 1;
244         for (i=0; i<20; ++i)
245         {
246                 start = lines.rfind('\n', start - 1);
247                 if (start == std::string::npos)
248                 {
249                         start = 0;
250                         break;
251                 }
252         }
253
254         font = new gFont("Regular", 14);
255         p.setFont(font);
256
257         p.renderText(usable_area, 
258                 lines.substr(start), gPainter::RT_HALIGN_LEFT);
259         sleep(10);
260
261         raise(SIGKILL);
262 }
263
264 #if defined(__MIPSEL__)
265 void oops(const mcontext_t &context, int dumpcode)
266 {
267         eDebug("PC: %08lx", (unsigned long)context.pc);
268         int i;
269         for (i=0; i<32; ++i)
270         {
271                 eDebugNoNewLine(" %08x", (int)context.gregs[i]);
272                 if ((i&3) == 3)
273                         eDebug("");
274         }
275                 /* this is temporary debug stuff. */
276         if (dumpcode && ((unsigned long)context.pc) > 0x10000) /* not a zero pointer */
277         {
278                 eDebug("As a final action, i will try to dump a bit of code.");
279                 eDebug("I just hope that this won't crash.");
280                 int i;
281                 eDebugNoNewLine("%08lx:", (unsigned long)context.pc);
282                 for (i=0; i<0x20; ++i)
283                         eDebugNoNewLine(" %02x", ((unsigned char*)context.pc)[i]);
284                 eDebug(" (end)");
285         }
286 }
287 #endif
288
289 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
290 {
291 #ifndef NO_OOPS_SUPPORT
292         ucontext_t *uc = (ucontext_t*)ctx;
293
294         oops(uc->uc_mcontext, signum == SIGSEGV || signum == SIGABRT);
295 #endif
296         eDebug("-------");
297         bsodFatal("enigma2, signal");
298 }
299
300 void bsodCatchSignals()
301 {
302         struct sigaction act;
303         act.sa_sigaction = handleFatalSignal;
304         act.sa_flags = SA_RESTART | SA_SIGINFO;
305         if (sigemptyset(&act.sa_mask) == -1)
306                 perror("sigemptyset");
307         
308                 /* start handling segfaults etc. */
309         sigaction(SIGSEGV, &act, 0);
310         sigaction(SIGILL, &act, 0);
311         sigaction(SIGBUS, &act, 0);
312         sigaction(SIGABRT, &act, 0);
313 }
314
315 void bsodLogInit()
316 {
317         logOutput.connect(addToLogbuffer);
318 }