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