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