Merge branch 'master' into experimental
[vuplus_dvbapp] / 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 "crashlog@dream-multimedia-tv.de"
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;
73
74         ePythonConfigQuery::getConfigValue(key.c_str(), value);
75         //we get at least the default value if python is still alive
76         if (!value.empty())
77                 return value;
78
79         value = defaultValue;
80
81         // get value from enigma2 settings file
82         std::ifstream in(eEnv::resolve("${sysconfdir}/enigma2/settings").c_str());
83         if (in.good()) {
84                 do {
85                         std::string line;
86                         std::getline(in, line);
87                         size_t size = key.size();
88                         if (!key.compare(0, size, line) && line[size] == '=') {
89                                 value = line.substr(size + 1);
90                                 break;
91                         }
92                 } while (in.good());
93                 in.close();
94         }
95
96         return value;
97 }
98
99 static bool getConfigBool(const std::string &key, bool defaultValue)
100 {
101         std::string value = getConfigString(key, defaultValue ? "true" : "false");
102         const char *cvalue = value.c_str();
103
104         if (!strcasecmp(cvalue, "true"))
105                 return true;
106         if (!strcasecmp(cvalue, "false"))
107                 return false;
108
109         return defaultValue;
110 }
111
112 void bsodFatal(const char *component)
113 {
114         std::ostringstream os;
115         os << time(0);
116
117         std::string logfile("/media/hdd/enigma2_crash_" + os.str() + ".log");
118
119         FILE *f = fopen(logfile.c_str(), "wb");
120         
121         std::string lines = getLogBuffer();
122         
123                 /* find python-tracebacks, and extract "  File "-strings */
124         size_t start = 0;
125         
126         std::string crash_emailaddr = CRASH_EMAILADDR;
127         std::string crash_component = "enigma2";
128
129         if (component)
130                 crash_component = component;
131         else
132         {
133                 while ((start = lines.find("\n  File \"", start)) != std::string::npos)
134                 {
135                         start += 9;
136                         size_t end = lines.find("\"", start);
137                         if (end == std::string::npos)
138                                 break;
139                         end = lines.rfind("/", end);
140                                 /* skip a potential prefix to the path */
141                         unsigned int path_prefix = lines.find("/usr/", start);
142                         if (path_prefix != std::string::npos && path_prefix < end)
143                                 start = path_prefix;
144
145                         if (end == std::string::npos)
146                                 break;
147
148                         std::string filename(lines.substr(start, end - start) + INFOFILE);
149                         std::ifstream in(filename.c_str());
150                         if (in.good()) {
151                                 std::getline(in, crash_emailaddr) && std::getline(in, crash_component);
152                                 in.close();
153                         }
154                 }
155         }
156
157         if (f)
158         {
159                 time_t t = time(0);
160                 struct tm tm;
161                 char tm_str[32];
162
163                 localtime_r(&t, &tm);
164                 strftime(tm_str, sizeof(tm_str), "%a %b %_d %T %Y", &tm);
165
166                 XmlGenerator xml(f);
167
168                 xml.open("opendreambox");
169
170                 xml.open("enigma2");
171                 xml.string("crashdate", tm_str);
172                 xml.string("compiledate", __DATE__);
173                 xml.string("contactemail", crash_emailaddr);
174                 xml.comment("Please email this crashlog to above address");
175
176                 xml.string("skin", getConfigString("config.skin.primary_skin", "Default Skin"));
177                 xml.string("sourcedate", enigma2_date);
178                 xml.string("branch", enigma2_branch);
179                 xml.string("rev", enigma2_rev);
180                 xml.string("version", PACKAGE_VERSION);
181                 xml.close();
182
183                 xml.open("image");
184                 xml.stringFromFile("dreamboxmodel", "/proc/stb/info/model");
185                 xml.stringFromFile("kernelcmdline", "/proc/cmdline");
186                 xml.stringFromFile("nimsockets", "/proc/bus/nim_sockets");
187                 if (!getConfigBool("config.plugins.crashlogautosubmit.sendAnonCrashlog", true)) {
188                         xml.cDataFromFile("dreamboxca", "/proc/stb/info/ca");
189                         xml.cDataFromFile("enigma2settings", eEnv::resolve("${sysconfdir}/enigma2/settings"), ".password=");
190                 }
191                 if (getConfigBool("config.plugins.crashlogautosubmit.addNetwork", false)) {
192                         xml.cDataFromFile("networkinterfaces", "/etc/network/interfaces");
193                         xml.cDataFromFile("dns", "/etc/resolv.conf");
194                         xml.cDataFromFile("defaultgateway", "/etc/default_gw");
195                 }
196                 if (getConfigBool("config.plugins.crashlogautosubmit.addWlan", false))
197                         xml.cDataFromFile("wpasupplicant", "/etc/wpa_supplicant.conf");
198                 xml.cDataFromFile("imageversion", "/etc/image-version");
199                 xml.cDataFromFile("imageissue", "/etc/issue.net");
200                 xml.close();
201
202                 xml.open("software");
203                 xml.cDataFromCmd("enigma2software", "opkg list_installed | grep enigma2");
204                 xml.cDataFromCmd("dreamboxsoftware", "opkg list_installed | grep dream");
205                 xml.cDataFromCmd("gstreamersoftware", "opkg list_installed | grep gst");
206                 xml.close();
207
208                 xml.open("crashlogs");
209                 xml.cDataFromString("enigma2crashlog", getLogBuffer());
210                 xml.cDataFromCmd("pythonMD5sum", "find " + eEnv::resolve("${libdir}/enigma2/python/") + " -name \"*.py\" | xargs md5sum");
211                 xml.close();
212
213                 xml.close();
214
215                 fclose(f);
216         }
217
218         ePtr<gMainDC> my_dc;
219         gMainDC::getInstance(my_dc);
220         
221         gPainter p(my_dc);
222         p.resetOffset();
223         p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
224         p.setBackgroundColor(gRGB(0x008000));
225         p.setForegroundColor(gRGB(0xFFFFFF));
226
227         ePtr<gFont> font = new gFont("Regular", 20);
228         p.setFont(font);
229         p.clear();
230
231         eRect usable_area = eRect(100, 70, my_dc->size().width() - 150, 100);
232         
233         std::string text("We are really sorry. Your Dreambox encountered "
234                 "a software problem, and needs to be restarted. "
235                 "Please send the logfile created in /hdd/ to " + crash_emailaddr + ".\n"
236                 "Your Dreambox restarts in 10 seconds!\n"
237                 "Component: " + crash_component);
238
239         p.renderText(usable_area, text.c_str(), gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
240
241         usable_area = eRect(100, 170, my_dc->size().width() - 180, my_dc->size().height() - 20);
242
243         int i;
244
245         start = std::string::npos + 1;
246         for (i=0; i<20; ++i)
247         {
248                 start = lines.rfind('\n', start - 1);
249                 if (start == std::string::npos)
250                 {
251                         start = 0;
252                         break;
253                 }
254         }
255
256         font = new gFont("Regular", 14);
257         p.setFont(font);
258
259         p.renderText(usable_area, 
260                 lines.substr(start), gPainter::RT_HALIGN_LEFT);
261         sleep(10);
262
263         raise(SIGKILL);
264 }
265
266 #if defined(__MIPSEL__)
267 void oops(const mcontext_t &context, int dumpcode)
268 {
269         eDebug("PC: %08lx", (unsigned long)context.pc);
270         int i;
271         for (i=0; i<32; ++i)
272         {
273                 eDebugNoNewLine(" %08x", (int)context.gregs[i]);
274                 if ((i&3) == 3)
275                         eDebug("");
276         }
277                 /* this is temporary debug stuff. */
278         if (dumpcode && ((unsigned long)context.pc) > 0x10000) /* not a zero pointer */
279         {
280                 eDebug("As a final action, i will try to dump a bit of code.");
281                 eDebug("I just hope that this won't crash.");
282                 int i;
283                 eDebugNoNewLine("%08lx:", (unsigned long)context.pc);
284                 for (i=0; i<0x20; ++i)
285                         eDebugNoNewLine(" %02x", ((unsigned char*)context.pc)[i]);
286                 eDebug(" (end)");
287         }
288 }
289 #endif
290
291 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
292 {
293 #ifndef NO_OOPS_SUPPORT
294         ucontext_t *uc = (ucontext_t*)ctx;
295
296         oops(uc->uc_mcontext, signum == SIGSEGV || signum == SIGABRT);
297 #endif
298         eDebug("-------");
299         bsodFatal("enigma2, signal");
300 }
301
302 void bsodCatchSignals()
303 {
304         struct sigaction act;
305         act.sa_sigaction = handleFatalSignal;
306         act.sa_flags = SA_RESTART | SA_SIGINFO;
307         if (sigemptyset(&act.sa_mask) == -1)
308                 perror("sigemptyset");
309         
310                 /* start handling segfaults etc. */
311         sigaction(SIGSEGV, &act, 0);
312         sigaction(SIGILL, &act, 0);
313         sigaction(SIGBUS, &act, 0);
314         sigaction(SIGABRT, &act, 0);
315 }
316
317 void bsodLogInit()
318 {
319         logOutput.connect(addToLogbuffer);
320 }