More debug cosmetics
[openblackhole/openblackhole-enigma2.git] / main / bsod.cpp
1 #include <csignal>
2 #include <fstream>
3 #include <sstream>
4 #include <lib/base/eenv.h>
5 #include <lib/base/eerror.h>
6 #include <lib/base/nconfig.h>
7 #include <lib/gdi/gmaindc.h>
8
9 #if defined(__MIPSEL__)
10 #include <asm/ptrace.h>
11 #else
12 #warning "no oops support!"
13 #define NO_OOPS_SUPPORT
14 #endif
15
16 #include "xmlgenerator.h"
17 #include "version_info.h"
18
19 /************************************************/
20
21 #define CRASH_EMAILADDR "forum at www.openpli.org"
22 #define INFOFILE "/maintainer.info"
23
24 #define RINGBUFFER_SIZE 16384
25 static char ringbuffer[RINGBUFFER_SIZE];
26 static unsigned int ringbuffer_head;
27
28 static void addToLogbuffer(const char *data, unsigned int len)
29 {
30         while (len)
31         {
32                 unsigned 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                 ASSERT(ringbuffer_head <= RINGBUFFER_SIZE);
42                 if (ringbuffer_head == RINGBUFFER_SIZE)
43                         ringbuffer_head = 0;
44         }
45 }
46
47 static const std::string getLogBuffer()
48 {
49         unsigned int begin = ringbuffer_head;
50         while (ringbuffer[begin] == 0)
51         {
52                 ++begin;
53                 if (begin == RINGBUFFER_SIZE)
54                         begin = 0;
55                 if (begin == ringbuffer_head)
56                         return "";
57         }
58
59         if (begin < ringbuffer_head)
60                 return std::string(ringbuffer + begin, ringbuffer_head - begin);
61         else
62                 return std::string(ringbuffer + begin, RINGBUFFER_SIZE - begin) + std::string(ringbuffer, ringbuffer_head);
63 }
64
65 static void addToLogbuffer(int level, const std::string &log)
66 {
67         addToLogbuffer(log.c_str(), log.size());
68 }
69
70 static const std::string getConfigString(const std::string &key, const std::string &defaultValue)
71 {
72         std::string value = eConfigManager::getConfigValue(key.c_str());
73
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 static bool bsodhandled = false;
112
113 void bsodFatal(const char *component)
114 {
115         /* show no more than one bsod while shutting down/crashing */
116         if (bsodhandled) return;
117         bsodhandled = true;
118
119         std::string lines = getLogBuffer();
120
121                 /* find python-tracebacks, and extract "  File "-strings */
122         size_t start = 0;
123
124         std::string crash_emailaddr = CRASH_EMAILADDR;
125         std::string crash_component = "enigma2";
126
127         if (component)
128                 crash_component = component;
129         else
130         {
131                 while ((start = lines.find("\n  File \"", start)) != std::string::npos)
132                 {
133                         start += 9;
134                         size_t end = lines.find("\"", start);
135                         if (end == std::string::npos)
136                                 break;
137                         end = lines.rfind("/", end);
138                                 /* skip a potential prefix to the path */
139                         unsigned int path_prefix = lines.find("/usr/", start);
140                         if (path_prefix != std::string::npos && path_prefix < end)
141                                 start = path_prefix;
142
143                         if (end == std::string::npos)
144                                 break;
145
146                         std::string filename(lines.substr(start, end - start) + INFOFILE);
147                         std::ifstream in(filename.c_str());
148                         if (in.good()) {
149                                 std::getline(in, crash_emailaddr) && std::getline(in, crash_component);
150                                 in.close();
151                         }
152                 }
153         }
154
155         FILE *f;
156         const char* crashlog_name;
157         std::ostringstream os;
158         os << "/media/hdd/enigma2_crash_";
159         os << time(0);
160         os << ".log";
161         crashlog_name = os.str().c_str();
162         f = fopen(crashlog_name, "wb");
163
164         if (f == NULL)
165         {
166                 /* No hardisk. If there is a crash log in /home/root, leave it
167                  * alone because we may be in a crash loop and writing this file
168                  * all night long may damage the flash. Also, usually the first
169                  * crash log is the most interesting one. */
170                 crashlog_name = "/home/root/enigma2_crash.log";
171                 if ((access(crashlog_name, F_OK) == 0) ||
172                     ((f = fopen(crashlog_name, "wb")) == NULL))
173                 {
174                         /* Re-write the same file in /tmp/ because it's expected to
175                          * be in RAM. So the first crash log will end up in /home
176                          * and the last in /tmp */
177                         crashlog_name = "/tmp/enigma2_crash.log";
178                         f = fopen(crashlog_name, "wb");
179                 }
180         }
181
182         if (f)
183         {
184                 time_t t = time(0);
185                 struct tm tm;
186                 char tm_str[32];
187
188                 localtime_r(&t, &tm);
189                 strftime(tm_str, sizeof(tm_str), "%a %b %_d %T %Y", &tm);
190
191                 XmlGenerator xml(f);
192
193                 xml.open("openpli");
194
195                 xml.open("enigma2");
196                 xml.string("crashdate", tm_str);
197                 xml.string("compiledate", __DATE__);
198                 xml.string("contactemail", crash_emailaddr);
199                 xml.comment("Please email this crashlog to above address");
200
201                 xml.string("skin", getConfigString("config.skin.primary_skin", "Default Skin"));
202                 xml.string("sourcedate", enigma2_date);
203                 xml.string("branch", enigma2_branch);
204                 xml.string("rev", enigma2_rev);
205                 xml.string("version", PACKAGE_VERSION);
206                 xml.close();
207
208                 xml.open("image");
209                 if(access("/proc/stb/info/boxtype", F_OK) != -1) {
210                         xml.stringFromFile("stbmodel", "/proc/stb/info/boxtype");
211                 }
212                 else if (access("/proc/stb/info/vumodel", F_OK) != -1) {
213                         xml.stringFromFile("stbmodel", "/proc/stb/info/vumodel");
214                 }
215                 else if (access("/proc/stb/info/model", F_OK) != -1) {
216                         xml.stringFromFile("stbmodel", "/proc/stb/info/model");
217                 }
218                 xml.cDataFromCmd("kernelversion", "uname -a");
219                 xml.stringFromFile("kernelcmdline", "/proc/cmdline");
220                 xml.stringFromFile("nimsockets", "/proc/bus/nim_sockets");
221                 xml.cDataFromFile("imageversion", "/etc/image-version");
222                 xml.cDataFromFile("imageissue", "/etc/issue.net");
223                 xml.close();
224
225                 xml.open("crashlogs");
226                 xml.cDataFromString("enigma2crashlog", getLogBuffer());
227                 xml.close();
228
229                 xml.close();
230
231                 fclose(f);
232         }
233
234         ePtr<gMainDC> my_dc;
235         gMainDC::getInstance(my_dc);
236
237         gPainter p(my_dc);
238         p.resetOffset();
239         p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
240         p.setBackgroundColor(gRGB(0x008000));
241         p.setForegroundColor(gRGB(0xFFFFFF));
242
243         int hd =  my_dc->size().width() == 1920;
244         ePtr<gFont> font = new gFont("Regular", hd ? 30 : 20);
245         p.setFont(font);
246         p.clear();
247
248         eRect usable_area = eRect(hd ? 30 : 100, hd ? 30 : 70, my_dc->size().width() - (hd ? 60 : 150), hd ? 150 : 100);
249
250         os.str("");
251         os.clear();
252         os << "We are really sorry. Your STB encountered "
253                 "a software problem, and needs to be restarted.\n"
254                 "Please send the logfile " << crashlog_name << " to " << crash_emailaddr << ".\n"
255                 "Your STB restarts in 10 seconds!\n"
256                 "Component: " << crash_component;
257
258         p.renderText(usable_area, os.str().c_str(), gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
259
260         usable_area = eRect(hd ? 30 : 100, hd ? 180 : 170, my_dc->size().width() - (hd ? 60 : 180), my_dc->size().height() - (hd ? 30 : 20));
261
262         int i;
263
264         start = std::string::npos + 1;
265         for (i=0; i<20; ++i)
266         {
267                 start = lines.rfind('\n', start - 1);
268                 if (start == std::string::npos)
269                 {
270                         start = 0;
271                         break;
272                 }
273         }
274
275         font = new gFont("Regular", hd ? 21 : 14);
276         p.setFont(font);
277
278         p.renderText(usable_area,
279                 lines.substr(start), gPainter::RT_HALIGN_LEFT);
280         sleep(10);
281
282         /*
283          * When 'component' is NULL, we are called because of a python exception.
284          * In that case, we'd prefer to to a clean shutdown of the C++ objects,
285          * and this should be safe, because the crash did not occur in the
286          * C++ part.
287          * However, when we got here for some other reason, a segfault probably,
288          * we prefer to stop immediately instead of performing a clean shutdown.
289          * We'd risk destroying things with every additional instruction we're
290          * executing here.
291          */
292         if (component) raise(SIGKILL);
293 }
294
295 #if defined(__MIPSEL__)
296 void oops(const mcontext_t &context)
297 {
298         eDebug("PC: %08lx", (unsigned long)context.pc);
299         int i;
300         for (i=0; i<32; i += 4)
301         {
302                 eDebug("    %08x %08x %08x %08x",
303                         (int)context.gregs[i+0], (int)context.gregs[i+1],
304                         (int)context.gregs[i+2], (int)context.gregs[i+3]);
305         }
306 }
307 #endif
308
309 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
310 {
311 #ifndef NO_OOPS_SUPPORT
312         ucontext_t *uc = (ucontext_t*)ctx;
313         oops(uc->uc_mcontext);
314 #endif
315         eDebug("-------FATAL SIGNAL");
316         bsodFatal("enigma2, signal");
317 }
318
319 void bsodCatchSignals()
320 {
321         struct sigaction act;
322         act.sa_sigaction = handleFatalSignal;
323         act.sa_flags = SA_RESTART | SA_SIGINFO;
324         if (sigemptyset(&act.sa_mask) == -1)
325                 perror("sigemptyset");
326
327                 /* start handling segfaults etc. */
328         sigaction(SIGSEGV, &act, 0);
329         sigaction(SIGILL, &act, 0);
330         sigaction(SIGBUS, &act, 0);
331         sigaction(SIGABRT, &act, 0);
332 }
333
334 void bsodLogInit()
335 {
336         logOutput.connect(addToLogbuffer);
337 }