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