Merge remote branch 'remotes/origin/acid-burn/bug_629_show_autofs_volumes_as_backuplo...
[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                 if (!getConfigBool("config.plugins.crashlogautosubmit.sendAnonCrashlog", true)) {
187                         xml.cDataFromFile("dreamboxca", "/proc/stb/info/ca");
188                         xml.cDataFromFile("enigma2settings", eEnv::resolve("${sysconfdir}/enigma2/settings"), ".password=");
189                 }
190                 if (getConfigBool("config.plugins.crashlogautosubmit.addNetwork", false)) {
191                         xml.cDataFromFile("networkinterfaces", "/etc/network/interfaces");
192                         xml.cDataFromFile("dns", "/etc/resolv.conf");
193                         xml.cDataFromFile("defaultgateway", "/etc/default_gw");
194                 }
195                 if (getConfigBool("config.plugins.crashlogautosubmit.addWlan", false))
196                         xml.cDataFromFile("wpasupplicant", "/etc/wpa_supplicant.conf");
197                 xml.cDataFromFile("imageversion", "/etc/image-version");
198                 xml.cDataFromFile("imageissue", "/etc/issue.net");
199                 xml.close();
200
201                 xml.open("software");
202                 xml.cDataFromCmd("enigma2software", "ipkg list_installed | grep enigma2");
203                 xml.cDataFromCmd("dreamboxsoftware", "ipkg list_installed | grep dream");
204                 xml.cDataFromCmd("gstreamersoftware", "ipkg list_installed | grep gst");
205                 xml.close();
206
207                 xml.open("crashlogs");
208                 xml.cDataFromString("enigma2crashlog", getLogBuffer());
209                 xml.cDataFromCmd("pythonMD5sum", "find " + eEnv::resolve("${libdir}/enigma2/python/") + " -name \"*.py\" | xargs md5sum");
210                 xml.close();
211
212                 xml.close();
213
214                 fclose(f);
215         }
216
217         ePtr<gMainDC> my_dc;
218         gMainDC::getInstance(my_dc);
219         
220         gPainter p(my_dc);
221         p.resetOffset();
222         p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
223         p.setBackgroundColor(gRGB(0x008000));
224         p.setForegroundColor(gRGB(0xFFFFFF));
225
226         ePtr<gFont> font = new gFont("Regular", 20);
227         p.setFont(font);
228         p.clear();
229
230         eRect usable_area = eRect(100, 70, my_dc->size().width() - 150, 100);
231         
232         std::string text("We are really sorry. Your Dreambox encountered "
233                 "a software problem, and needs to be restarted. "
234                 "Please send the logfile created in /hdd/ to " + crash_emailaddr + ".\n"
235                 "Your Dreambox restarts in 10 seconds!\n"
236                 "Component: " + crash_component);
237
238         p.renderText(usable_area, text.c_str(), gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
239
240         usable_area = eRect(100, 170, my_dc->size().width() - 180, my_dc->size().height() - 20);
241
242         int i;
243
244         start = std::string::npos + 1;
245         for (i=0; i<20; ++i)
246         {
247                 start = lines.rfind('\n', start - 1);
248                 if (start == std::string::npos)
249                 {
250                         start = 0;
251                         break;
252                 }
253         }
254
255         font = new gFont("Regular", 14);
256         p.setFont(font);
257
258         p.renderText(usable_area, 
259                 lines.substr(start), gPainter::RT_HALIGN_LEFT);
260         sleep(10);
261
262         raise(SIGKILL);
263 }
264
265 #if defined(__MIPSEL__)
266 void oops(const mcontext_t &context, int dumpcode)
267 {
268         eDebug("PC: %08lx", (unsigned long)context.pc);
269         int i;
270         for (i=0; i<32; ++i)
271         {
272                 eDebugNoNewLine(" %08x", (int)context.gregs[i]);
273                 if ((i&3) == 3)
274                         eDebug("");
275         }
276                 /* this is temporary debug stuff. */
277         if (dumpcode && ((unsigned long)context.pc) > 0x10000) /* not a zero pointer */
278         {
279                 eDebug("As a final action, i will try to dump a bit of code.");
280                 eDebug("I just hope that this won't crash.");
281                 int i;
282                 eDebugNoNewLine("%08lx:", (unsigned long)context.pc);
283                 for (i=0; i<0x20; ++i)
284                         eDebugNoNewLine(" %02x", ((unsigned char*)context.pc)[i]);
285                 eDebug(" (end)");
286         }
287 }
288 #endif
289
290 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
291 {
292 #ifndef NO_OOPS_SUPPORT
293         ucontext_t *uc = (ucontext_t*)ctx;
294
295         oops(uc->uc_mcontext, signum == SIGSEGV || signum == SIGABRT);
296 #endif
297         eDebug("-------");
298         bsodFatal("enigma2, signal");
299 }
300
301 void bsodCatchSignals()
302 {
303         struct sigaction act;
304         act.sa_sigaction = handleFatalSignal;
305         act.sa_flags = SA_RESTART | SA_SIGINFO;
306         if (sigemptyset(&act.sa_mask) == -1)
307                 perror("sigemptyset");
308         
309                 /* start handling segfaults etc. */
310         sigaction(SIGSEGV, &act, 0);
311         sigaction(SIGILL, &act, 0);
312         sigaction(SIGBUS, &act, 0);
313         sigaction(SIGABRT, &act, 0);
314 }
315
316 void bsodLogInit()
317 {
318         logOutput.connect(addToLogbuffer);
319 }