ff4e20f33b3b7cb3897c9acb2b7ef49c99352894
[citadel.git] / libcitadel / lib / wildfire.c
1 /*@{*/
2
3 #include "sysdep.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <string.h>
12
13 #if HAVE_BACKTRACE
14 #include <execinfo.h>
15 #endif
16
17 #include "libcitadel.h"
18 #include "libcitadellocal.h"
19
20
21 ConstStr WF_MsgStrs[] = {
22         {HKEY("LOG")},
23         {HKEY("INFO")},
24         {HKEY("WARN")},
25         {HKEY("ERROR")},
26         {HKEY("TRACE")},
27         {HKEY("EXCEPTION")}
28 };
29
30 static JsonValue *WFInfo(const char *Filename, long fnlen,
31                          long LineNo, 
32                          WF_MessageType Type)
33 {
34         JsonValue *Val;
35
36         Val = NewJsonObject(NULL, 0);
37         JsonObjectAppend(Val, 
38                          NewJsonPlainString(HKEY("Type"),
39                                             WF_MsgStrs[Type].Key, 
40                                             WF_MsgStrs[Type].len));
41         JsonObjectAppend(Val, 
42                          NewJsonPlainString(HKEY("File"), 
43                                             Filename, fnlen));
44         JsonObjectAppend(Val, 
45                          NewJsonNumber(HKEY("Line"), LineNo));
46         return Val;
47 }
48                             
49
50 JsonValue *WildFireMessage(const char *Filename, long fnlen,
51                            long LineNo,
52                            StrBuf *Msg, 
53                            WF_MessageType Type)
54 {
55         JsonValue *Ret;
56
57         Ret = NewJsonArray(NULL, 0);
58         JsonArrayAppend(Ret, WFInfo(Filename, fnlen,
59                                     LineNo, Type));
60
61         JsonArrayAppend(Ret, 
62                         NewJsonString(NULL, 0, Msg));
63         return Ret;
64 }
65
66 JsonValue *WildFireMessagePlain(const char *Filename, long fnlen,
67                                 long LineNo,
68                                 const char *Message, long len, 
69                                 WF_MessageType Type)
70 {
71         JsonValue *Val;
72         Val = NewJsonArray(NULL, 0);
73
74         JsonArrayAppend(Val, WFInfo(Filename, fnlen,
75                                     LineNo, Type));
76         JsonArrayAppend(Val, 
77                         NewJsonPlainString(NULL, 0, Message, len));
78         return Val;
79 }
80
81 void WildFireAddArray(JsonValue *ReportBase, JsonValue *Array, WF_MessageType Type)
82 {
83         JsonValue *Val;
84         Val = NewJsonArray(NULL, 0);
85         JsonArrayAppend(Val, 
86                         NewJsonPlainString(NULL, 0, 
87                                            WF_MsgStrs[Type].Key, 
88                                            WF_MsgStrs[Type].len));
89
90         JsonArrayAppend(Val, Array);
91 }
92
93 int addr2line_write_pipe[2];
94 int addr2line_read_pipe[2];
95 pid_t addr2line_pid;
96
97 #ifdef HAVE_BACKTRACE
98 /* 
99  * Start up the addr2line daemon so we can decode function pointers
100  */
101 static void start_addr2line_daemon(const char *binary) 
102 {
103         struct stat filestats;
104         int i;
105         const char *addr2line = "/usr/bin/addr2line";
106         const char minuse[] = "-e";
107
108         printf("Starting addr2line daemon for decoding of backtraces\n");
109
110         if ((stat(addr2line, &filestats)==-1) ||
111             (filestats.st_size==0)){
112                 printf("didn't find addr2line daemon in %s: %s\n", addr2line, strerror(errno));
113                 abort();
114         }
115         if (pipe(addr2line_write_pipe) != 0) {
116                 printf("Unable to create pipe for addr2line daemon: %s\n", strerror(errno));
117                 abort();
118         }
119         if (pipe(addr2line_read_pipe) != 0) {
120                 printf("Unable to create pipe for addr2line daemon: %s\n", strerror(errno));
121                 abort();
122         }
123
124         addr2line_pid = fork();
125         if (addr2line_pid < 0) {
126                 printf("Unable to fork addr2line daemon: %s\n", strerror(errno));
127                 abort();
128         }
129         if (addr2line_pid == 0) {
130                 dup2(addr2line_write_pipe[0], 0);
131                 dup2(addr2line_read_pipe[1], 1);
132                 for (i=2; i<256; ++i) close(i);
133                 execl(addr2line, addr2line, minuse, binary, NULL);
134                 printf("Unable to exec addr2line daemon: %s\n", strerror(errno));
135                 abort();
136                 exit(errno);
137         }
138 }
139
140 static int addr2lineBacktrace(StrBuf *Function, 
141                               StrBuf *FileName, 
142                               StrBuf *Pointer, 
143                               StrBuf *Buf,
144                               unsigned int *FunctionLine)
145
146 {
147         const char *err;
148         const char *pch, *pche;
149
150         write(addr2line_write_pipe[1], SKEY(Pointer));
151         if (StrBufTCP_read_line(Buf, &addr2line_read_pipe[0], 0, &err) <= 0)
152         {
153                 StrBufAppendBufPlain(Buf, err, -1, 0);
154                 return 0;
155         }
156         pch = ChrPtr(Buf);
157         pche = strchr(pch, ':');
158         FlushStrBuf(FileName);
159         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
160         if (pche != NULL)
161         {
162                 pche++;
163                 *FunctionLine = atoi(pche);
164         }
165         else 
166                 *FunctionLine = 0;
167         return 1;
168 }
169
170 static int ParseBacktrace(char *Line, 
171                           StrBuf *Function, 
172                           StrBuf *FileName, 
173                           unsigned int *FunctionLine)
174 {
175         char *pch, *pche;
176
177         pch = Line;
178         pche = strchr(pch, '(');
179         if (pche == NULL) return 0;
180         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
181         pch = pche + 1;
182         pche = strchr(pch, '+');
183         if (pche == NULL) return 0;
184         StrBufAppendBufPlain(Function, pch, pche - pch, 0);
185         pch = pche + 1;
186         pche = strchr(pch, ')');
187         if (pche == NULL) return 0;
188         *pche = '\0';
189         sscanf(pch, "%x", FunctionLine);
190         StrBufAppendBufPlain(Function, pche + 1, -1, 0);
191         return 1;
192 }
193 #endif
194 long BaseFrames = 0;
195 StrBuf *FullBinaryName = NULL;
196
197 void WildFireShutdown(void)
198 {
199         close(addr2line_write_pipe[0]);
200         close(addr2line_read_pipe[0]);
201
202         FreeStrBuf(&FullBinaryName);
203 }
204
205 void WildFireInitBacktrace(const char *argvNull, int AddBaseFrameSkip)
206 {
207
208 #ifdef HAVE_BACKTRACE
209         void *stack_frames[100];
210         size_t size;
211         long i;
212         char **strings;
213         StrBuf *FileName;
214         StrBuf *Function;
215         StrBuf *Pointer;
216         StrBuf *Buf;
217         unsigned int FunctionLine;
218         struct stat filestats;
219
220         FileName = NewStrBuf();
221         Function = NewStrBuf();
222         Pointer = NewStrBuf();
223         Buf = NewStrBuf();
224
225         BaseFrames = size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
226         BaseFrames --;
227         BaseFrames += AddBaseFrameSkip;
228         strings = backtrace_symbols(stack_frames, size);
229         for (i = 1; i < size; i++) {
230                 if (strings != NULL){
231                         ParseBacktrace(strings[i], Function, 
232                                        FileName, 
233                                        &FunctionLine);
234                         FullBinaryName = NewStrBufDup(FileName);
235                         size = i;
236                 }
237                 else {
238                         char path[256];
239                         getcwd(path, sizeof(path));
240                         FullBinaryName = NewStrBufPlain(path, -1);
241                         StrBufAppendBufPlain(FullBinaryName, HKEY("/"), 0);
242                         StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
243                         i = size;
244                  }
245         }
246         if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) ||
247             (filestats.st_size==0)){
248                 FlushStrBuf(FullBinaryName);
249                 StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
250                 if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) ||
251                     (filestats.st_size==0)){
252                         FlushStrBuf(FullBinaryName);
253                         fprintf(stderr, "unable to open my binary for addr2line checking, verbose backtraces won't work.\n");
254                 }
255         }
256         free(strings);
257         FreeStrBuf(&FileName);
258         FreeStrBuf(&Function);
259         FreeStrBuf(&Pointer);
260         FreeStrBuf(&Buf);
261         if (StrLength(FullBinaryName) > 0)
262                 start_addr2line_daemon(ChrPtr(FullBinaryName));
263 #endif
264
265
266 }
267
268
269 JsonValue *WildFireException(const char *Filename, long FileLen,
270                              long LineNo,
271                              StrBuf *Message,
272                              int StackOffset)
273 {
274         JsonValue *ExcClass;
275         JsonValue *Val;
276         Val = NewJsonArray(NULL, 0);
277
278         JsonArrayAppend(Val, WFInfo(Filename, FileLen,
279                                     LineNo, eEXCEPTION));
280
281         ExcClass = NewJsonObject(WF_MsgStrs[eTRACE].Key, 
282                                  WF_MsgStrs[eTRACE].len);
283         
284         JsonArrayAppend(Val, ExcClass);
285         JsonObjectAppend(ExcClass, 
286                          NewJsonPlainString(HKEY("Class"), 
287                                             HKEY("Exception")));
288         JsonObjectAppend(ExcClass, 
289                          NewJsonString(HKEY("Message"), Message));
290         JsonObjectAppend(ExcClass, 
291                          NewJsonPlainString(HKEY("File"), 
292                                             Filename, FileLen));
293 /*
294         JsonObjectAppend(ExcClass, 
295                          NewJsonPlainString(HKEY("Type"), 
296                                             HKEY("throw")));
297 */
298         JsonObjectAppend(ExcClass, 
299                          NewJsonNumber(HKEY("Line"), LineNo));
300
301 #ifdef HAVE_BACKTRACE
302         {
303                 void *stack_frames[100];
304                 size_t size;
305                 long i;
306                 char **strings;
307                 JsonValue *Trace;
308                 JsonValue *Frame;
309                 StrBuf *FileName;
310                 StrBuf *Function;
311                 StrBuf *Pointer;
312                 StrBuf *Buf;
313                 unsigned int FunctionLine;
314
315                 Trace = NewJsonArray(HKEY("Trace"));
316                 JsonObjectAppend(ExcClass, Trace);
317                 FileName = NewStrBuf();
318                 Function = NewStrBuf();
319                 Pointer = NewStrBuf();
320                 Buf = NewStrBuf();
321
322                 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
323                 strings = backtrace_symbols(stack_frames, size);
324                 for (i = StackOffset + 1; i < size; i++) {
325                         if (strings != NULL){
326                                 ParseBacktrace(strings[i], Function, 
327                                                FileName,
328                                                &FunctionLine);
329                                 
330                         }
331                         StrBufPrintf(Pointer, "%p\n", stack_frames[i]);
332                         
333                         addr2lineBacktrace(Function, 
334                                            FileName, 
335                                            Pointer, 
336                                            Buf, 
337                                            &FunctionLine);
338
339                         Frame = NewJsonObject(NULL, 0);
340                         JsonArrayAppend(Trace, Frame);
341                         JsonObjectAppend(Frame, 
342                                          NewJsonString(HKEY("function"), Function));
343                         JsonObjectAppend(Frame, 
344                                          NewJsonString(HKEY("file"), FileName));
345                         JsonObjectAppend(Frame, 
346                                          NewJsonNumber(HKEY("line"), FunctionLine));
347                         JsonObjectAppend(Frame, 
348                                          NewJsonArray(HKEY("args")));/* not supportet... */
349
350                         FunctionLine = 0;
351                         FlushStrBuf(FileName);
352                         FlushStrBuf(Function);
353                         FlushStrBuf(Pointer);
354                 }
355                 free(strings);
356                 FreeStrBuf(&FileName);
357                 FreeStrBuf(&Function);
358                 FreeStrBuf(&Pointer);
359                 FreeStrBuf(&Buf);
360         }
361 #endif
362         return Val;
363 }
364
365 void WildFireSerializePayload(StrBuf *JsonBuffer, StrBuf *OutBuf, int *MsgCount, AddHeaderFunc AddHdr)
366 {
367         int n = *MsgCount;
368         StrBuf *Buf;
369         StrBuf *HeaderName;
370         StrBuf *N; 
371         const char Concatenate[] = "\\";
372         const char empty[] = "";
373         const char *Cat;
374         StrBuf *Header;
375
376         Header = NewStrBuf();
377         if (*MsgCount == 0) {
378                 if (OutBuf != NULL) {
379                         StrBufAppendBufPlain(OutBuf, 
380                                              HKEY( 
381                                                      "X-Wf-Protocol-1" 
382                                                      ": "
383                                                      "http://meta.wildfirehq.org/Protocol/JsonStream/0.2\r\n"), 0);
384                         StrBufAppendBufPlain(OutBuf, 
385                                              HKEY( 
386                                                      "X-Wf-1-Plugin-1" 
387                                                      ": " 
388                                                      "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0\r\n"), 0);
389                         StrBufAppendBufPlain(OutBuf, 
390                                              HKEY(
391                                                      "X-Wf-1-Structure-1"
392                                                      ": "
393                                                      "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1\r\n"), 0);
394                 }
395                 else {
396                         AddHdr("X-Wf-Protocol-1", 
397                                "http://meta.wildfirehq.org/Protocol/JsonStream/0.2");
398                         AddHdr("X-Wf-1-Plugin-1",
399                                "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0");
400                         AddHdr("X-Wf-1-Structure-1",
401                                "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
402                 }
403         }
404
405         N = NewStrBuf();
406         StrBufPrintf(N, "%d", StrLength(JsonBuffer));
407         Buf = NewStrBufPlain(NULL, 1024);
408         HeaderName = NewStrBuf();
409
410         while (StrLength(JsonBuffer) > 0) {
411                 FlushStrBuf(Buf);
412                 StrBufPrintf(HeaderName, "X-Wf-"WF_MAJOR"-"WF_STRUCTINDEX"-"WF_SUB"-%d", n);
413                 if (StrLength(JsonBuffer) > 800) {
414                         StrBufAppendBufPlain(Buf, ChrPtr(JsonBuffer), 800, 0);
415                         StrBufCutLeft(JsonBuffer, 800);
416                         Cat = Concatenate;
417                 }
418                 else {
419                         StrBufAppendBuf(Buf, JsonBuffer, 0);
420                         FlushStrBuf(JsonBuffer);
421                         Cat = empty;
422                 }
423                 if (OutBuf != NULL) {
424                         StrBufAppendPrintf(OutBuf, 
425                                            "%s: %s|%s|%s\r\n", 
426                                            ChrPtr(HeaderName), 
427                                            ChrPtr(N),
428                                            ChrPtr(Buf), 
429                                            Cat);
430                 }
431                 else {
432                         StrBufAppendPrintf(Header, 
433                                            "%s|%s|%s", 
434                                            ChrPtr(N),
435                                            ChrPtr(Buf), 
436                                            Cat);
437                         AddHdr(ChrPtr(HeaderName), ChrPtr(Header));
438                         
439                 }
440
441                 FlushStrBuf(N);
442                 n++;
443         }
444         *MsgCount = n;
445         if (OutBuf == NULL) {
446                 FreeStrBuf(&Header);
447         }
448         FreeStrBuf(&N);
449         FreeStrBuf(&Buf);
450         FreeStrBuf(&HeaderName);
451 }
452
453
454
455
456
457
458 /* this is how we do it...
459 void CreateWildfireSampleMessage(void)
460 {
461         JsonValue *Error;
462                 
463         StrBuf *Buf;
464         StrBuf *Header;
465         StrBuf *Json;
466         int n = 1;
467
468         Header = NewStrBuf();
469         Json = NewStrBuf();
470
471         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
472         SerializeJson(Json, Error);
473         WildFireSerializePayload(Json, Header, &n, NULL);
474         StrBufAppendBuf(WC->HBuf, Header, 0);
475         DeleteJSONValue(Error);
476         FlushStrBuf(Json);
477         FlushStrBuf(Header);
478
479         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__,  HKEY("Warn message"), eWARN);
480         SerializeJson(Json, Error);
481         WildFireSerializePayload(Json, Header, &n, NULL);
482         StrBufAppendBuf(WC->HBuf, Header, 0);
483         DeleteJSONValue(Error);
484         FlushStrBuf(Json);
485         FlushStrBuf(Header);
486
487         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Error message"), eERROR);
488         SerializeJson(Json, Error);
489         WildFireSerializePayload(Json, Header, &n, NULL);
490         StrBufAppendBuf(WC->HBuf, Header, 0);
491         DeleteJSONValue(Error);
492         FlushStrBuf(Json);
493         FlushStrBuf(Header);
494
495         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
496         SerializeJson(Json, Error);
497         WildFireSerializePayload(Json, Header, &n, NULL);
498         StrBufAppendBuf(WC->HBuf, Header, 0);
499         DeleteJSONValue(Error);
500         FlushStrBuf(Json);
501         FlushStrBuf(Header);
502
503         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
504         SerializeJson(Json, Error);
505         WildFireSerializePayload(Json, Header, &n, NULL);
506         StrBufAppendBuf(WC->HBuf, Header, 0);
507         DeleteJSONValue(Error);
508         FlushStrBuf(Json);
509         FlushStrBuf(Header);
510
511
512         Buf = NewStrBufPlain(HKEY("test error message"));
513         Error = WildFireException(Buf, HKEY(__FILE__), __LINE__, 1);
514         SerializeJson(Json, Error);
515         WildFireSerializePayload(Json, Header, &n, NULL);
516         StrBufAppendBuf(WC->HBuf, Header, 0);
517         DeleteJSONValue(Error);
518
519         FlushStrBuf(Json);
520         FlushStrBuf(Header);
521
522 }
523
524 */