Merge commit '97b86324d322309843cc5d9d93b039d706c5ae58' into mm
[openblackhole/openblackhole-enigma2.git] / main / bsod.cpp
1 #include <string.h>
2 #include <signal.h>
3 #include <asm/ptrace.h>
4
5 #include <lib/base/eerror.h>
6 #include <lib/base/smartptr.h>
7 #include <lib/base/nconfig.h>
8 #include <lib/gdi/grc.h>
9 #include <lib/gdi/gfbdc.h>
10 #ifdef WITH_SDL
11 #include <lib/gdi/sdl.h>
12 #endif
13
14 #include "version.h"
15
16 /************************************************/
17
18 #define CRASH_EMAILADDR "crashlog@dream-multimedia-tv.de"
19 #define STDBUFFER_SIZE 512
20 #define RINGBUFFER_SIZE 16384
21 static char ringbuffer[RINGBUFFER_SIZE];
22 static int ringbuffer_head;
23
24 static void addToLogbuffer(const char *data, int len)
25 {
26         while (len)
27         {
28                 int remaining = RINGBUFFER_SIZE - ringbuffer_head;
29         
30                 if (remaining > len)
31                         remaining = len;
32         
33                 memcpy(ringbuffer + ringbuffer_head, data, remaining);
34                 len -= remaining;
35                 data += remaining;
36                 ringbuffer_head += remaining;
37                 if (ringbuffer_head >= RINGBUFFER_SIZE)
38                         ringbuffer_head = 0;
39         }
40 }
41
42 static std::string getLogBuffer()
43 {
44         int begin = ringbuffer_head;
45         while (ringbuffer[begin] == 0)
46         {
47                 ++begin;
48                 if (begin == RINGBUFFER_SIZE)
49                         begin = 0;
50                 if (begin == ringbuffer_head)
51                         return "";
52         }
53         if (begin < ringbuffer_head)
54                 return std::string(ringbuffer + begin, ringbuffer_head - begin);
55         else
56         {
57                 return std::string(ringbuffer + begin, RINGBUFFER_SIZE - begin) + std::string(ringbuffer, ringbuffer_head);
58         }
59 }
60
61 static void addToLogbuffer(int level, const std::string &log)
62 {
63         addToLogbuffer(log.c_str(), log.size());
64 }
65
66 static std::string getConfigFileValue(const char *entry)
67 {
68         std::string configfile = "/etc/enigma2/settings";
69         std::string configvalue;
70         if (entry)
71         {
72                 ePythonConfigQuery::getConfigValue(entry, configvalue);
73                 if (configvalue != "") //we get at least the default value if python is still alive
74                 {
75                         return configvalue;
76                 }
77                 else // get value from enigma2 settings file
78                 {
79                         FILE *f = fopen(configfile.c_str(), "r");
80                         if (!f)
81                         {
82                                 return "Error";
83                         }
84                         while (1)
85                         {
86                                 char line[1024];
87                                 if (!fgets(line, 1024, f))
88                                         break;
89                                 if (!strncmp(line, entry, strlen(entry) ))
90                                 {
91                                         if (strlen(line) && line[strlen(line)-1] == '\r')
92                                                 line[strlen(line)-1] = 0;
93                                         if (strlen(line) && line[strlen(line)-1] == '\n')
94                                                 line[strlen(line)-1] = 0;
95                                         std::string tmp = line;
96                                         int posEqual = tmp.find("=", 0);
97                                         configvalue = tmp.substr(posEqual+1);
98                                 }
99                         }
100                         fclose(f);
101                         return configvalue;
102                 }
103         }
104         return "";
105 }
106
107 static std::string getFileContent(const char *file)
108 {
109         std::string filecontent;
110
111         if (file)
112         {
113                 FILE *f = fopen(file, "r");
114                 if (!f)
115                 {
116                         return "Error";
117                 }
118                 while (1)
119                 {
120                         char line[1024];
121                         if (!fgets(line, 1024, f))
122                                 break;
123                         std::string tmp = line;
124                         std::string password;
125                         size_t pwdpos = tmp.find(".password=", 0);
126                         if( pwdpos != std::string::npos)
127                         {
128                                 filecontent += tmp.substr(0,pwdpos +10);
129                                 for ( size_t pos = pwdpos +10; pos < tmp.length()-1; ++pos )
130                                 {
131                                         filecontent += "X";
132                                 }
133                                 filecontent += "\n";
134                         }
135                         else {
136                                 filecontent += line;
137                         }
138                 }
139                 fclose(f);
140         }
141         return filecontent;
142 }
143
144 static std::string execCommand(char* cmd) {
145         FILE* pipe = popen(cmd, "r");
146         if (!pipe)
147                 return "Error";
148         char buffer[STDBUFFER_SIZE];
149         std::string result = "";
150         while(!feof(pipe))
151         {
152                 if(!fgets(buffer,STDBUFFER_SIZE, pipe))
153                         break;
154                 result += buffer;
155         }
156         pclose(pipe);
157         return result;
158 }
159
160 extern std::string execCommand();
161 extern std::string getConfigFileValue();
162 extern std::string getFileContent();
163 extern std::string getLogBuffer();
164
165 #define INFOFILE "/maintainer.info"
166
167 static bool bsodhandled = false;
168
169 void bsodFatal(const char *component)
170 {
171         /* show no more than one bsod while shutting down/crashing */
172         if (bsodhandled) return;
173         bsodhandled = true;
174
175         char logfile[128];
176         sprintf(logfile, "/media/hdd/enigma2_crash_%u.log", (unsigned int)time(0));
177         FILE *f = fopen(logfile, "wb");
178         
179         std::string lines = getLogBuffer();
180         
181                 /* find python-tracebacks, and extract "  File "-strings */
182         size_t start = 0;
183         
184         char crash_emailaddr[256] = CRASH_EMAILADDR;
185         char crash_component[256] = "enigma2";
186
187         if (component)
188                 snprintf(crash_component, 256, component);
189         else
190         {
191                 while ((start = lines.find("\n  File \"", start)) != std::string::npos)
192                 {
193                         start += 9;
194                         size_t end = lines.find("\"", start);
195                         if (end == std::string::npos)
196                                 break;
197                         end = lines.rfind("/", end);
198                                 /* skip a potential prefix to the path */
199                         unsigned int path_prefix = lines.find("/usr/", start);
200                         if (path_prefix != std::string::npos && path_prefix < end)
201                                 start = path_prefix;
202
203                         if (end == std::string::npos)
204                                 break;
205                         if (end - start >= (256 - strlen(INFOFILE)))
206                                 continue;
207                         char filename[256];
208                         snprintf(filename, 256, "%s%s", lines.substr(start, end - start).c_str(), INFOFILE);
209                         FILE *cf = fopen(filename, "r");
210                         if (cf)
211                         {
212                                 fgets(crash_emailaddr, sizeof crash_emailaddr, cf);
213                                 if (*crash_emailaddr && crash_emailaddr[strlen(crash_emailaddr)-1] == '\n')
214                                         crash_emailaddr[strlen(crash_emailaddr)-1] = 0;
215
216                                 fgets(crash_component, sizeof crash_component, cf);
217                                 if (*crash_component && crash_component[strlen(crash_component)-1] == '\n')
218                                         crash_component[strlen(crash_component)-1] = 0;
219                                 fclose(cf);
220                         }
221                 }
222         }
223
224         if (f)
225         {
226                 time_t t = time(0);
227                 char crashtime[STDBUFFER_SIZE];
228                 sprintf(crashtime, "%s",ctime(&t));
229                 if (strlen(crashtime) && crashtime[strlen(crashtime)-1] == '\n')
230                                 crashtime[strlen(crashtime)-1] = 0;
231                 fprintf(f, "<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>\n<opendreambox>\n");
232                 fprintf(f, "\t<enigma2>\n");
233                 fprintf(f, "\t\t<crashdate>%s</crashdate>\n", crashtime);
234 #ifdef ENIGMA2_CHECKOUT_TAG
235                 fprintf(f, "\t\t<checkouttag>" ENIGMA2_CHECKOUT_TAG "</checkouttag>\n");
236 #else
237                 fprintf(f, "\t\t<compiledate>" __DATE__ "</compiledate>\n");
238 #endif
239 #ifdef ENIGMA2_CHECKOUT_ROOT
240                 fprintf(f, "\t\t<checkoutroot>" ENIGMA2_CHECKOUT_ROOT "</checkoutroot>\n");
241 #endif
242                 fprintf(f, "\t\t<contactemail>%s</contactemail>\n", crash_emailaddr);
243                 fprintf(f, "\t\t<!-- Please email this crashlog to above address -->\n");
244                 std::string activeSkin = getConfigFileValue("config.skin.primary_skin");
245                 if (activeSkin != "Error")
246                 {
247                         if (activeSkin == "")
248                                 activeSkin = "Default Skin";
249                         fprintf(f, "\t\t<skin>%s</skin>\n", activeSkin.c_str());
250                 }
251                 fprintf(f, "\t</enigma2>\n");
252
253                 fprintf(f, "\t<image>\n");
254                 std::string model = getFileContent("/proc/stb/info/model");
255                 if (model != "Error")
256                 {
257                         char modelname[STDBUFFER_SIZE];
258                         sprintf(modelname, "%s",model.c_str());
259                         if (strlen(modelname) && modelname[strlen(modelname)-1] == '\n')
260                                 modelname[strlen(modelname)-1] = 0;
261                         fprintf(f, "\t\t<dreamboxmodel>%s</dreamboxmodel>\n", modelname);
262                 }
263                 std::string kernel = getFileContent("/proc/cmdline");
264                 if (kernel != "Error")
265                 {
266                         char kernelcmd[STDBUFFER_SIZE];
267                         sprintf(kernelcmd, "%s",kernel.c_str());
268                         if (strlen(kernelcmd) && kernelcmd[strlen(kernelcmd)-1] == '\n')
269                                 kernelcmd[strlen(kernelcmd)-1] = 0;
270                         fprintf(f, "\t\t<kernelcmdline>%s</kernelcmdline>\n", kernelcmd);
271                 }
272                 std::string sendAnonCrashlog = getConfigFileValue("config.plugins.crashlogautosubmit.sendAnonCrashlog");
273                 if (sendAnonCrashlog == "False" || sendAnonCrashlog == "false") // defaults to true... default anonymized crashlogs
274                 {
275                         std::string ca = getFileContent("/proc/stb/info/ca");
276                         if (ca != "Error")
277                         {
278                                 char dreamboxca[STDBUFFER_SIZE];
279                                 sprintf(dreamboxca, "%s",ca.c_str());
280                                 if (strlen(dreamboxca) && dreamboxca[strlen(dreamboxca)-1] == '\n')
281                                         dreamboxca[strlen(dreamboxca)-1] = 0;
282                                 fprintf(f, "\t\t<dreamboxca>\n\t\t<![CDATA[\n%s\n\t\t]]>\n\t\t</dreamboxca>\n", dreamboxca);
283                         }
284                         std::string settings = getFileContent("/etc/enigma2/settings");
285                         if (settings != "Error")
286                         {
287                                 fprintf(f, "\t\t<enigma2settings>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</enigma2settings>\n", settings.c_str());
288                         }
289                 }
290                 std::string addNetwork = getConfigFileValue("config.plugins.crashlogautosubmit.addNetwork");
291                 if (addNetwork == "True" || addNetwork == "true")
292                 {
293                         std::string nwinterfaces = getFileContent("/etc/network/interfaces");
294                         if (nwinterfaces != "Error")
295                         {
296                                 fprintf(f, "\t\t<networkinterfaces>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</networkinterfaces>\n", nwinterfaces.c_str());
297                         }
298                         std::string dns = getFileContent("/etc/resolv.conf");
299                         if (dns != "Error")
300                         {
301                                 fprintf(f, "\t\t<dns>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</dns>\n", dns.c_str());
302                         }
303                         std::string defaultgw = getFileContent("/etc/default_gw");
304                         if (defaultgw != "Error")
305                         {
306                                 char gateway[STDBUFFER_SIZE];
307                                 sprintf(gateway, "%s",defaultgw.c_str());
308                                 if (strlen(gateway) && gateway[strlen(gateway)-1] == '\n')
309                                         gateway[strlen(gateway)-1] = 0;
310                                 fprintf(f, "\t\t<defaultgateway>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</defaultgateway>\n", gateway);
311                         }
312                 }
313                 std::string addWlan = getConfigFileValue("config.plugins.crashlogautosubmit.addWlan");
314                 if (addWlan == "True" || addWlan == "true")
315                 {
316                         std::string wpasupplicant = getFileContent("/etc/wpa_supplicant.conf");
317                         if (wpasupplicant != "Error")
318                         {
319                                 fprintf(f, "\t\t<wpasupplicant>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</wpasupplicant>\n", wpasupplicant.c_str());
320                         }
321                 }
322                 std::string imageversion = getFileContent("/etc/image-version");
323                 if (imageversion != "Error")
324                 {
325                         fprintf(f, "\t\t<imageversion>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</imageversion>\n", imageversion.c_str());
326                 }
327                 std::string imageissue = getFileContent("/etc/issue.net");
328                 if (imageissue != "Error")
329                 {
330                         fprintf(f, "\t\t<imageissue>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</imageissue>\n", imageissue.c_str());
331                 }
332                 fprintf(f, "\t</image>\n");
333
334                 bool detailedCrash = getConfigFileValue("config.crash.details") == "true";
335                 if (detailedCrash)
336                 {
337                         fprintf(f, "\t<software>\n");
338                         std::string installedplugins = execCommand("ipkg list_installed 'enigma2*'");
339                         fprintf(f, "\t\t<enigma2software>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</enigma2software>\n", installedplugins.c_str());
340                         std::string dreambox = execCommand("ipkg list_installed 'dream*'");
341                         fprintf(f, "\t\t<dreamboxsoftware>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</dreamboxsoftware>\n", dreambox.c_str());
342                         std::string gstreamer = execCommand("ipkg list_installed 'gst*'");
343                         fprintf(f, "\t\t<gstreamersoftware>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</gstreamersoftware>\n", gstreamer.c_str());
344                         fprintf(f, "\t</software>\n");
345                 }
346
347                 fprintf(f, "\t<crashlogs>\n");
348                 fprintf(f, "\t\t<enigma2crashlog>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</enigma2crashlog>\n", getLogBuffer().c_str());
349                 if (detailedCrash)
350                 {
351                         std::string pythonmd5 = execCommand("find /usr/lib/enigma2/python/ -name \"*.py\" | xargs md5sum");
352                         fprintf(f, "\t\t<pythonMD5sum>\n\t\t<![CDATA[\n%s\t\t]]>\n\t\t</pythonMD5sum>\n", pythonmd5.c_str());
353                 }
354                 fprintf(f, "\t</crashlogs>\n");
355
356                 fprintf(f, "\n</opendreambox>\n");
357                 fclose(f);
358                 
359         }
360         
361 #ifdef WITH_SDL
362         ePtr<gSDLDC> my_dc;
363         gSDLDC::getInstance(my_dc);
364 #else
365         ePtr<gFBDC> my_dc;
366         gFBDC::getInstance(my_dc);
367 #endif
368         
369         {
370                 gPainter p(my_dc);
371                 p.resetOffset();
372                 p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
373 #ifdef ENIGMA2_CHECKOUT_TAG
374                 if (ENIGMA2_CHECKOUT_TAG[0] == 'T') /* tagged checkout (release) */
375                         p.setBackgroundColor(gRGB(0x0000C0));
376                 else if (ENIGMA2_CHECKOUT_TAG[0] == 'D') /* dated checkout (daily experimental build) */
377                 {
378                         srand(time(0));
379                         int r = rand();
380                         unsigned int col = 0;
381                         if (r & 1)
382                                 col |= 0x800000;
383                         if (r & 2)
384                                 col |= 0x008000;
385                         if (r & 4)
386                                 col |= 0x0000c0;
387                         p.setBackgroundColor(gRGB(col));
388                 }
389 #else
390                         p.setBackgroundColor(gRGB(0x008000));
391 #endif
392
393                 p.setForegroundColor(gRGB(0xFFFFFF));
394         
395                 ePtr<gFont> font = new gFont("Regular", 20);
396                 p.setFont(font);
397                 p.clear();
398         
399                 eRect usable_area = eRect(100, 70, my_dc->size().width() - 150, 100);
400                 
401                 char text[512];
402                 snprintf(text, 512, "We are really sorry. Your Dreambox encountered "
403                         "a software problem, and needs to be restarted. "
404                         "Please send the logfile created in /hdd/ to %s.\n"
405                         "Your Dreambox restarts in 10 seconds!\n"
406                         "Component: %s",
407                         crash_emailaddr, crash_component);
408         
409                 p.renderText(usable_area, text, gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
410         
411                 usable_area = eRect(100, 170, my_dc->size().width() - 180, my_dc->size().height() - 20);
412         
413                 int i;
414         
415                 size_t start = std::string::npos + 1;
416                 for (i=0; i<20; ++i)
417                 {
418                         start = lines.rfind('\n', start - 1);
419                         if (start == std::string::npos)
420                         {
421                                 start = 0;
422                                 break;
423                         }
424                 }
425         
426                 font = new gFont("Regular", 14);
427                 p.setFont(font);
428         
429                 p.renderText(usable_area, 
430                         lines.substr(start), gPainter::RT_HALIGN_LEFT);
431                 sleep(10);
432         }
433
434         /*
435          * When 'component' is NULL, we are called because of a python exception.
436          * In that case, we'd prefer to to a clean shutdown of the C++ objects,
437          * and this should be safe, because the crash did not occur in the
438          * C++ part.
439          * However, when we got here for some other reason, a segfault probably,
440          * we prefer to stop immediately instead of performing a clean shutdown.
441          * We'd risk destroying things with every additional instruction we're
442          * executing here.
443          */
444         if (component) raise(SIGKILL);
445 }
446
447 #if defined(__MIPSEL__)
448 void oops(const mcontext_t &context, int dumpcode)
449 {
450         eDebug("PC: %08lx", (unsigned long)context.pc);
451         int i;
452         for (i=0; i<32; ++i)
453         {
454                 eDebugNoNewLine(" %08x", (int)context.gregs[i]);
455                 if ((i&3) == 3)
456                         eDebug(" ");
457         }
458                 /* this is temporary debug stuff. */
459         if (dumpcode && ((unsigned long)context.pc) > 0x10000) /* not a zero pointer */
460         {
461                 eDebug("As a final action, i will try to dump a bit of code.");
462                 eDebug("I just hope that this won't crash.");
463                 int i;
464                 eDebugNoNewLine("%08lx:", (unsigned long)context.pc);
465                 for (i=0; i<0x20; ++i)
466                         eDebugNoNewLine(" %02x", ((unsigned char*)context.pc)[i]);
467                 eDebug(" (end)");
468         }
469 }
470 #else
471 #warning "no oops support!"
472 #define NO_OOPS_SUPPORT
473 #endif
474
475 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
476 {
477         ucontext_t *uc = (ucontext_t*)ctx;
478
479 #ifndef NO_OOPS_SUPPORT
480         oops(uc->uc_mcontext, signum == SIGSEGV || signum == SIGABRT);
481 #endif
482         eDebug("-------");
483         bsodFatal("enigma2, signal");
484 }
485
486 void bsodCatchSignals()
487 {
488         struct sigaction act;
489         act.sa_handler = SIG_DFL;
490         act.sa_sigaction = handleFatalSignal;
491         act.sa_flags = SA_RESTART | SA_SIGINFO;
492         if (sigemptyset(&act.sa_mask) == -1)
493                 perror("sigemptyset");
494         
495                 /* start handling segfaults etc. */
496         sigaction(SIGSEGV, &act, 0);
497         sigaction(SIGILL, &act, 0);
498         sigaction(SIGBUS, &act, 0);
499         sigaction(SIGABRT, &act, 0);
500 }
501
502 void bsodLogInit()
503 {
504         logOutput.connect(addToLogbuffer);
505 }