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